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/views/nodes/RecordingBox/RecordingBox.tsx | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

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

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>
-- 
cgit v1.2.3-70-g09d2


From 872dc82886f8111da1bdfd282ac2a78ec3bdc1f6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 29 Nov 2023 11:55:44 -0500
Subject: cleaned up presElementBox and did more this.rootDoc =>
 this.Document/layoutDoc

---
 src/client/documents/Documents.ts                  |   6 +-
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/views/DocComponent.tsx                  |   8 -
 src/client/views/InkingStroke.tsx                  |  24 +-
 src/client/views/PropertiesView.tsx                |   2 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |  14 +-
 src/client/views/nodes/ColorBox.tsx                |   2 +-
 src/client/views/nodes/EquationBox.tsx             |  10 +-
 src/client/views/nodes/LabelBox.tsx                |  18 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  20 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   3 +-
 src/client/views/nodes/WebBox.tsx                  |  54 ++--
 .../views/nodes/formattedText/DashFieldView.tsx    |   2 +-
 .../views/nodes/importBox/ImportElementBox.tsx     |   4 +-
 src/client/views/nodes/trails/PresBox.tsx          |   4 +
 src/client/views/nodes/trails/PresElementBox.tsx   | 332 ++++++++++-----------
 16 files changed, 242 insertions(+), 263 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4def19384..9107bd6ab 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -168,8 +168,10 @@ type DTYPEt = DTypeInfo | string;
 export class DocumentOptions {
     // coordinate and dimensions depending on view
     x?: NUMt = new NumInfo('x coordinate of document in a freeform view', false);
-    y?: NUMt = new NumInfo('y coordinage of document in a freeform view', false);
+    y?: NUMt = new NumInfo('y coordinate of document in a freeform view', false);
     z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, false, [1, 0]);
+    overlayX?: NUMt = new NumInfo('x coordinate of document in a overlay view', false);
+    overlayY?: NUMt = new NumInfo('y coordinate of document in a overlay view', false);
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", false);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
     latitude?: NUMt = new NumInfo('latitude coordinate for map views', false);
@@ -198,6 +200,7 @@ export class DocumentOptions {
     author_date?: DATEt = new DateInfo('date the document was created', true);
     annotationOn?: DOCt = new DocInfo('document annotated by this document', false);
     embedContainer?: DOCt = new DocInfo('document that displays (contains) this discument', false);
+    rootDocument?: DOCt = new DocInfo('document that supplies the information needed for a rendering template (eg, pres slide for PresElement)');
     color?: STRt = new StrInfo('foreground color data doc', false);
     hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection', false);
     backgroundColor?: STRt = new StrInfo('background color for data doc', false);
@@ -272,6 +275,7 @@ export class DocumentOptions {
     icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)', false);
     mediaState?: STRt = new StrInfo(`status of audio/video media document: ${media_state.PendingRecording}, ${media_state.Recording}, ${media_state.Paused}, ${media_state.Playing}`, false);
     recording?: BOOLt = new BoolInfo('whether WebCam is recording or not');
+    slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)');
     autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.');
     dontPlayLinkOnSelect?: BOOLt = new BoolInfo('whether an audio/video should start playing when a link is followed to it.');
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2f2ac2a71..80ec1e7bb 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -274,7 +274,7 @@ export class CurrentUserUtils {
             {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _layout_fitWidth: true, }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
             {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
-            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
+            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
             {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}},
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 61b7a3bff..397ac47ab 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -26,10 +26,6 @@ export function DocComponent<P extends DocComponentProps>() {
         @computed get Document() {
             return this.props.Document;
         }
-        // This is the "The Document" -- it encapsulates, data, layout, and any templates
-        @computed get rootDoc() {
-            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() {
             return this.props.LayoutTemplateString ? this.props.Document : Doc.Layout(this.props.Document, this.props.LayoutTemplate?.());
@@ -63,10 +59,6 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
         @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;
-        }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
         @computed get layoutDoc() {
             return Doc.Layout(this.props.Document);
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index cf565abc8..e081961e5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -82,24 +82,24 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const subAnchor = this._subContentView?.getAnchor?.(addAsAnnotation);
-        if (subAnchor !== this.rootDoc && subAnchor) return subAnchor;
+        if (subAnchor !== this.Document && subAnchor) return subAnchor;
 
-        if (!addAsAnnotation && !pinProps) return this.rootDoc;
+        if (!addAsAnnotation && !pinProps) return this.Document;
 
         const anchor = Docs.Create.ConfigDocument({
-            title: 'Ink anchor:' + this.rootDoc.title,
+            title: 'Ink anchor:' + this.Document.title,
             // set presentation timing for restoring shape
             presentation_duration: 1100,
             presentation_transition: 1000,
-            annotationOn: this.rootDoc,
+            annotationOn: this.Document,
         });
         if (anchor) {
             anchor.backgroundColor = 'transparent';
             // /* addAsAnnotation &&*/ this.addDocument(anchor);
-            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), inkable: true } }, this.rootDoc);
+            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), inkable: true } }, this.Document);
             return anchor;
         }
-        return this.rootDoc;
+        return this.Document;
     };
 
     /**
@@ -245,8 +245,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      * factor for converting between ink and screen space.
      */
     inkScaledData = () => {
-        const inkData = Cast(this.rootDoc[this.fieldKey], InkField)?.inkData ?? [];
-        const inkStrokeWidth = NumCast(this.rootDoc.stroke_width, 1);
+        const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
+        const inkStrokeWidth = NumCast(this.layoutDoc.stroke_width, 1);
         const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2;
         const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2;
         const inkLeft = Math.min(...inkData.map(p => p.X)) - inkStrokeWidth / 2;
@@ -362,7 +362,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
     @computed get fillColor() {
         const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
-        return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
+        return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FillColor) ?? 'transparent';
     }
     @computed get strokeColor() {
         const { inkData } = this.inkScaledData();
@@ -387,7 +387,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 this.layoutDoc._height = Math.round(NumCast(this.layoutDoc._height));
             });
         }
-        const highlight = !this.controlUndo && this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
+        const highlight = !this.controlUndo && this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Highlighting);
         const highlightIndex = highlight?.highlightIndex;
         const highlightColor = !this.props.isSelected() && !isInkMask && highlight?.highlightIndex ? highlight?.highlightColor : undefined;
         const color = StrCast(this.layoutDoc.stroke_outlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent');
@@ -457,7 +457,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             onContextMenu: () => {
                 const cm = ContextMenu.Instance;
                 !Doc.noviceMode && cm?.addItem({ description: 'Recognize Writing', event: this.analyzeStrokes, icon: 'paint-brush' });
-                cm?.addItem({ description: 'Toggle Mask', event: () => InkingStroke.toggleMask(this.rootDoc), icon: 'paint-brush' });
+                cm?.addItem({ description: 'Toggle Mask', event: () => InkingStroke.toggleMask(this.dataDoc), icon: 'paint-brush' });
                 cm?.addItem({ description: 'Edit Points', event: action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton)), icon: 'paint-brush' });
             },
         };
@@ -474,7 +474,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     {clickableLine(this.onPointerDown, isInkMask)}
                     {isInkMask ? null : inkLine}
                 </svg>
-                {!closed || (!RTFCast(this.rootDoc.text)?.Text && (!this.props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
+                {!closed || (!RTFCast(this.dataDoc.text)?.Text && (!this.props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
                     <div
                         className="inkStroke-text"
                         style={{
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8f37a0564..76ea9123d 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -71,7 +71,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
-        if (PresBox.Instance?.selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
+        if (PresBox.Instance?.selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.Document);
         return undefined;
     }
     @computed get isPres(): boolean {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 7346c4f12..03697b40a 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -38,11 +38,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
     }
 
     @computed get schemaDoc() {
-        return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
+        return this.props.DocumentView?.().props.docViewPath().lastElement()?.Document;
     }
 
     @computed get rowIndex() {
-        return this.schemaView?.rowIndex(this.rootDoc) ?? -1;
+        return this.schemaView?.rowIndex(this.Document) ?? -1;
     }
 
     componentDidMount(): void {
@@ -52,7 +52,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
     select = (ctrlKey: boolean, shiftKey: boolean) => {
         if (!this.schemaView) return;
         const lastSelected = Array.from(this.schemaView._selectedDocs).lastElement();
-        if (shiftKey && lastSelected) this.schemaView.selectRows(this.rootDoc, lastSelected);
+        if (shiftKey && lastSelected) this.schemaView.selectRows(this.Document, lastSelected);
         else {
             this.props.select?.(ctrlKey);
         }
@@ -107,7 +107,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    row && this.schemaView?.addRowRef?.(this.rootDoc, row);
+                    row && this.schemaView?.addRowRef?.(this.Document, row);
                     this._ref = row;
                 }}>
                 <div
@@ -128,7 +128,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                                 emptyFunction,
                                 undoable(e => {
                                     e.stopPropagation();
-                                    this.props.removeDocument?.(this.rootDoc);
+                                    this.props.removeDocument?.(this.Document);
                                 }, 'Delete Row')
                             )
                         }
@@ -145,7 +145,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                                 emptyFunction,
                                 undoable(e => {
                                     e.stopPropagation();
-                                    this.props.addDocTab(this.rootDoc, OpenWhere.addRight);
+                                    this.props.addDocTab(this.Document, OpenWhere.addRight);
                                 }, 'Open schema Doc preview')
                             )
                         }
@@ -155,7 +155,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
                     {this.schemaView?.columnKeys?.map((key, index) => (
                         <SchemaTableCell
                             key={key}
-                            Document={this.rootDoc}
+                            Document={this.Document}
                             col={index}
                             fieldKey={key}
                             allowCRs={false} // to enter text with new lines, must use \n
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 23ffd1080..4c7ea4002 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -51,7 +51,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     render() {
-        const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / NumCast(this.rootDoc._height), this.props.PanelWidth() / NumCast(this.rootDoc._width));
+        const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / NumCast(this.Document._height), this.props.PanelWidth() / NumCast(this.Document._width));
         return (
             <div
                 className={`colorBox-container${this.props.isContentActive() ? '-interactive' : ''}`}
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index d347c285b..cd263f82a 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -21,11 +21,11 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     _ref: React.RefObject<EquationEditor> = React.createRef();
     componentDidMount() {
         this.props.setContentView?.(this);
-        if (EquationBox.SelectOnLoad === this.rootDoc[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
+        if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
             this.props.select(false);
 
             this._ref.current!.mathField.focus();
-            this.rootDoc.text === 'x' && this._ref.current!.mathField.select();
+            this.dataDoc.text === 'x' && this._ref.current!.mathField.select();
             EquationBox.SelectOnLoad = '';
         }
         reaction(
@@ -66,7 +66,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             e.stopPropagation();
         }
         if (e.key === 'Tab') {
-            const graph = Docs.Create.FunctionPlotDocument([this.rootDoc], {
+            const graph = Docs.Create.FunctionPlotDocument([this.Document], {
                 x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width),
                 y: NumCast(this.layoutDoc.y),
                 _width: 400,
@@ -76,7 +76,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.props.addDocument?.(graph);
             e.stopPropagation();
         }
-        if (e.key === 'Backspace' && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
+        if (e.key === 'Backspace' && !this.dataDoc.text) this.props.removeDocument?.(this.Document);
     };
     @undoBatch
     onChange = (str: string) => (this.dataDoc.text = str);
@@ -111,7 +111,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     width: 'fit-content', // `${100 / scale}%`,
                     height: `${100 / scale}%`,
                     pointerEvents: !this.props.isSelected() ? 'none' : undefined,
-                    fontSize: StrCast(this.rootDoc._text_fontSize),
+                    fontSize: StrCast(this.layoutDoc._text_fontSize),
                 }}
                 onKeyDown={e => e.stopPropagation()}>
                 <EquationEditor ref={this._ref} value={StrCast(this.dataDoc.text, 'x')} spaceBehavesLikeTab={true} onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 4439be0cd..0d7b2b0a4 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -37,7 +37,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
     }
 
     getTitle() {
-        return this.rootDoc.title_custom ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
+        return this.dataDoc.title_custom ? StrCast(this.Document.title) : this.props.label ? this.props.label : typeof this.dataDoc[this.fieldKey] === 'string' ? StrCast(this.dataDoc[this.fieldKey]) : StrCast(this.Document.title);
     }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -81,16 +81,16 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
 
     @observable _mouseOver = false;
     @computed get hoverColor() {
-        return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
     }
 
     fitTextToBox = (r: any) => {
-        const singleLine = BoolCast(this.rootDoc._singleLine, true);
+        const singleLine = BoolCast(this.layoutDoc._singleLine, true);
         const params = {
             rotateText: null,
             fontSizeFactor: 1,
-            minimumFontSize: NumCast(this.rootDoc._label_minFontSize, 8),
-            maximumFontSize: NumCast(this.rootDoc._label_maxFontSize, 1000),
+            minimumFontSize: NumCast(this.layoutDoc._label_minFontSize, 8),
+            maximumFontSize: NumCast(this.layoutDoc._label_maxFontSize, 1000),
             limitingDimension: 'both',
             horizontalAlign: 'center',
             verticalAlign: 'center',
@@ -137,10 +137,10 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
                         fontFamily: StrCast(this.layoutDoc._text_fontFamily) || 'inherit',
                         letterSpacing: StrCast(this.layoutDoc.letterSpacing),
                         textTransform: StrCast(this.layoutDoc.textTransform) as any,
-                        paddingLeft: NumCast(this.rootDoc._xPadding),
-                        paddingRight: NumCast(this.rootDoc._xPadding),
-                        paddingTop: NumCast(this.rootDoc._yPadding),
-                        paddingBottom: NumCast(this.rootDoc._yPadding),
+                        paddingLeft: NumCast(this.layoutDoc._xPadding),
+                        paddingRight: NumCast(this.layoutDoc._xPadding),
+                        paddingTop: NumCast(this.layoutDoc._yPadding),
+                        paddingBottom: NumCast(this.layoutDoc._yPadding),
                         width: this.props.PanelWidth(),
                         height: this.props.PanelHeight(),
                         whiteSpace: typeof boxParams !== 'number' && boxParams.singleLine ? 'pre' : 'pre-wrap',
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index fd7d13655..cf157c3a9 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -33,12 +33,12 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get linkSource() {
-        return this.props.docViewPath()[this.props.docViewPath().length - 2].rootDoc; // this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
+        return this.props.docViewPath()[this.props.docViewPath().length - 2].Document; // this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
     }
 
     onPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => {
-            if (doubleTap) LinkFollower.FollowLink(this.rootDoc, this.linkSource, false);
+            if (doubleTap) LinkFollower.FollowLink(this.Document, this.linkSource, false);
             else this.props.select(false);
         });
     };
@@ -49,15 +49,15 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             if (separation > 100) {
-                const dragData = new DragManager.DocumentDragData([this.rootDoc]);
+                const dragData = new DragManager.DocumentDragData([this.Document]);
                 dragData.dropAction = 'embed';
                 dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                 return true;
             } else {
-                this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
-                this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
-                this.rootDoc.link_autoMoveAnchors = false;
+                this.layoutDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
+                this.layoutDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
+                this.layoutDoc.link_autoMoveAnchors = false;
             }
         }
         return false;
@@ -68,8 +68,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     render() {
         TraceMobx();
         const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView
-        const x = NumCast(this.rootDoc[this.fieldKey + '_x'], 100);
-        const y = NumCast(this.rootDoc[this.fieldKey + '_y'], 100);
+        const x = NumCast(this.layoutDoc[this.fieldKey + '_x'], 100);
+        const y = NumCast(this.layoutDoc[this.fieldKey + '_y'], 100);
         const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor');
         const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
         const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
@@ -88,7 +88,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     LinkDocPreview.SetLinkInfo({
                         docProps: this.props,
                         linkSrc: this.linkSource,
-                        linkDoc: this.rootDoc,
+                        linkDoc: this.Document,
                         showHeader: true,
                         location: [e.clientX, e.clientY + 20],
                         noPreview: false,
@@ -97,7 +97,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 onPointerDown={this.onPointerDown}
                 onContextMenu={this.specificContextMenu}
                 style={{
-                    border: selView && this.rootDoc[selView] === this.rootDoc[this.fieldKey] ? `solid ${globalCssVariables.MEDIUM_GRAY} 2px` : undefined,
+                    border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${globalCssVariables.MEDIUM_GRAY} 2px` : undefined,
                     background,
                     left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
                     top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 3838ce4ee..f01642236 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -13,7 +13,6 @@ import { DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
-import { SelectionManager } from '../../../util/SelectionManager';
 import { Presentation } from '../../../util/TrackMovements';
 import { undoBatch } from '../../../util/UndoManager';
 import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
@@ -193,7 +192,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     render() {
         return (
-            <div className="recordingBox" ref={this._ref}>
+            <div className="recordingBox" style={{ width: '100%' }} ref={this._ref}>
                 {!this.result && (
                     <RecordingView
                         forceTrackScreen={BoolCast(this.layoutDoc[this.fieldKey + '_trackScreen'])}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 5c46035be..1e6d92327 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -95,7 +95,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.allAnnotations.filter(a => a.text_inlineAnnotations);
     }
     @computed get webField() {
-        return Cast(this.rootDoc[this.props.fieldKey], WebField)?.url;
+        return Cast(this.Document[this.props.fieldKey], WebField)?.url;
     }
 
     constructor(props: any) {
@@ -145,7 +145,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
         }
         this.layoutDoc.thumb = undefined;
-        this.rootDoc.thumbLockout = true; // lock to prevent multiple thumb updates.
+        this.Document.thumbLockout = true; // lock to prevent multiple thumb updates.
         CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop)
             .then((data_url: any) => {
                 if (data_url.includes('<!DOCTYPE')) {
@@ -155,7 +155,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 Utils.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename =>
                     setTimeout(
                         action(() => {
-                            this.rootDoc.thumbLockout = false;
+                            this.Document.thumbLockout = false;
                             this.layoutDoc.thumb = new ImageField(returnedfilename);
                             this.layoutDoc.thumbScrollTop = scrollTop;
                             this.layoutDoc.thumbNativeWidth = nativeWidth;
@@ -185,11 +185,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         });
         this._disposers.urlchange = reaction(
-            () => WebCast(this.rootDoc.data),
+            () => WebCast(this.dataDoc.data),
             url => this.submitURL(false, false)
         );
         this._disposers.titling = reaction(
-            () => StrCast(this.rootDoc.title),
+            () => StrCast(this.Document.title),
             url => {
                 url.startsWith('www') && this.setData('http://' + url);
                 url.startsWith('http') && this.setData(url);
@@ -251,7 +251,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
         if (this._mainCont.current && selRange) {
-            if (this.rootDoc[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);
@@ -281,7 +281,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     focus = (anchor: Doc, options: DocFocusOptions) => {
-        if (anchor !== this.rootDoc && this._outerRef.current) {
+        if (anchor !== this.Document && this._outerRef.current) {
             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) {
@@ -298,8 +298,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     getView = (doc: Doc) => {
-        if (Doc.AreProtosEqual(doc, this.rootDoc)) return new Promise<Opt<DocumentView>>(res => res(this.props.DocumentView?.()));
-        if (this.rootDoc.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);
@@ -326,11 +326,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchor =
             visibleAnchor ??
             Docs.Create.ConfigDocument({
-                title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._layout_scrollTop),
+                title: StrCast(this.Document.title + ' ' + this.layoutDoc._layout_scrollTop),
                 y: NumCast(this.layoutDoc._layout_scrollTop),
-                annotationOn: this.rootDoc,
+                annotationOn: this.Document,
             });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.rootDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.Document);
         anchor.text = ele?.textContent ?? '';
         anchor.text_html = ele?.innerHTML ?? this._selectionText;
         addAsAnnotation && this.addDocumentWrapper(anchor);
@@ -369,7 +369,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         // bcz: NEED TO unrotate e.clientX and e.clientY
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
-        this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
+        this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.Document);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
 
         if (!word && !(e.target as any)?.className?.includes('rangeslider') && !(e.target as any)?.onclick && !(e.target as any)?.parentNode?.onclick) {
@@ -401,14 +401,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     iframeDown = (e: PointerEvent) => {
         this.props.select(false);
         const locpt = { 
-            x: (e.clientX / NumCast(this.rootDoc.nativeWidth)) * this.props.PanelWidth(), 
-            y: ((e.clientY - NumCast(this.rootDoc.layout_scrollTop))/ NumCast(this.rootDoc.nativeHeight)) * this.props.PanelHeight() }; // prettier-ignore
+            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()
             .inverse()
-            .transformPoint(NumCast(this.rootDoc.width) / 2, NumCast(this.rootDoc.height) / 2)!;
+            .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 theclick = [theclickoff.x + scrcent[0], theclickoff.y + scrcent[1]];
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
@@ -488,15 +488,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const initHeights = () => {
                 this._scrollHeight = Math.max(this._scrollHeight, iframeContent.body.scrollHeight || 0);
                 if (this._scrollHeight) {
-                    this.rootDoc.nativeHeight = Math.min(NumCast(this.rootDoc.nativeHeight), 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.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth));
                 }
             };
             const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0);
             if (swidth) {
-                const aspectResize = swidth / NumCast(this.rootDoc.nativeWidth);
-                this.rootDoc.nativeWidth = swidth;
-                this.rootDoc.nativeHeight = NumCast(this.rootDoc.nativeHeight) * aspectResize;
+                const aspectResize = swidth / NumCast(this.Document.nativeWidth);
+                this.Document.nativeWidth = swidth;
+                this.Document.nativeHeight = NumCast(this.Document.nativeHeight) * aspectResize;
                 this.layoutDoc.height = NumCast(this.layoutDoc._height) * aspectResize;
             }
             initHeights();
@@ -586,8 +586,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     forward = (checkAvailable?: boolean) => {
-        const future = Cast(this.rootDoc[this.fieldKey + '_future'], listSpec('string'), []);
-        const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
+        const future = Cast(this.dataDoc[this.fieldKey + '_future'], listSpec('string'), []);
+        const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []);
         if (checkAvailable) return future.length;
         runInAction(() => {
             if (future.length) {
@@ -760,7 +760,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
         if (sel?.empty) sel.empty(); // Chrome
         else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
-        this._setPreviewCursor?.(x ?? 0, y ?? 0, false, !this._marqueeref.current?.isEmpty, this.rootDoc);
+        this._setPreviewCursor?.(x ?? 0, y ?? 0, false, !this._marqueeref.current?.isEmpty, this.Document);
         if (x !== undefined && y !== undefined) {
             ContextMenu.Instance.closeMenu();
             ContextMenu.Instance.setIgnoreEvents(false);
@@ -788,7 +788,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._webPageHasBeenRendered = true;
             })
         );
-        const field = this.rootDoc[this.props.fieldKey];
+        const field = this.dataDoc[this.props.fieldKey];
         if (field instanceof HtmlField) {
             return (
                 <span
@@ -891,7 +891,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     toggleSidebar = action((preview: boolean = false) => {
         var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         if (!nativeWidth) {
-            const defaultNativeWidth = NumCast(this.rootDoc.nativeWidth, this.rootDoc[this.fieldKey] instanceof WebField ? 850 : NumCast(this.Document._width));
+            const defaultNativeWidth = NumCast(this.Document.nativeWidth, this.dataDoc[this.fieldKey] instanceof WebField ? 850 : NumCast(this.Document._width));
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
             Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (NumCast(this.Document._height) / NumCast(this.Document._width)) * defaultNativeWidth);
             nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
@@ -1096,7 +1096,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.rootDoc, this.props, StyleProp.BackgroundColor) }} />
+                <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }} />
                 <div
                     className="webBox-container"
                     style={{
@@ -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}
-                            Document={this.rootDoc}
+                            Document={this.Document}
                             anchorMenuClick={this.anchorMenuClick}
                             scrollTop={NumCast(this.layoutDoc.layout_scrollTop)}
                             annotationLayerScrollTop={0}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 0a64c8062..130ac40c8 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -108,7 +108,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         if (this.props.docId) {
             DocServer.GetRefField(this.props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
         } else {
-            this._dashDoc = this.props.tbox.rootDoc;
+            this._dashDoc = this.props.tbox.Document;
         }
     }
     componentWillUnmount() {
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index 58f0b29e4..170626cd2 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -22,7 +22,7 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 <DocumentView
                     {...this.props} //
                     LayoutTemplateString={undefined}
-                    Document={this.rootDoc}
+                    Document={this.Document}
                     isContentActive={returnFalse}
                     DataDoc={undefined}
                     addDocument={returnFalse}
@@ -33,6 +33,6 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
     }
     render() {
-        return !(this.rootDoc instanceof Doc) ? null : this.mainItem;
+        return !(this.Document instanceof Doc) ? null : this.mainItem;
     }
 }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index bb0ed0943..71c585c38 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -36,6 +36,7 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { ScriptingBox } from '../ScriptingBox';
 import './PresBox.scss';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
+import _ = require('lodash');
 const { Howl } = require('howler');
 
 export interface pinDataTypes {
@@ -1075,6 +1076,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     };
+
     childLayoutTemplate = () => Docs.Create.PresElementBoxDocument();
     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
@@ -2549,6 +2551,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
+    SlideIndex = (slideDoc: Doc) => DocListCast(this.dataDoc[this.presFieldKey]).indexOf(slideDoc);
+
     RemFromMap = (treeViewDoc: Doc, index: number[]) => {
         if (!treeViewDoc.presentation_targetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element.
         if (!this._unmounting && this.isTree) {
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f5faeb611..6a8edee0d 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -34,37 +34,53 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(PresElementBox, fieldKey);
     }
-    _heightDisposer: IReactionDisposer | undefined;
+    private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
+    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+    private _titleRef: React.RefObject<EditableView> = React.createRef();
+    private _heightDisposer: IReactionDisposer | undefined;
+    readonly expandViewHeight = 100;
+    readonly collapsedHeight = 35;
 
     @observable _dragging = false;
-    // Idea: this boolean will determine whether to automatically show the video when this preselement is selected.
-    // @observable static showVideo: boolean = false;
-    @computed get indexInPres() {
-        return DocListCast(this.presBox?.[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc);
-    } // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
-    @computed get expandViewHeight() {
-        return 100;
-    }
-    @computed get collapsedHeight() {
-        return 35;
-    } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
-    @computed get selectedArray() {
-        return this.presBoxView?.selectedArray;
-    }
+
+    // the presentation view that renders this slide
     @computed get presBoxView() {
         return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as PresBox;
     }
+
+    // the presentation view document that renders this slide
     @computed get presBox() {
-        return this.props.DocumentView?.().props.docViewPath().lastElement()?.Document;
+        return this.presBoxView?.Document;
     }
+
+    // Since this node is being rendered with a template, this method retrieves
+    // the actual slide being rendered from the auto-generated rendering template
+    @computed get slideDoc() {
+        return Cast(this.Document.rootDocument, Doc, null) || this.props.Document;
+    }
+
+    // this is the document in the workspaces that is targeted by the slide
     @computed get targetDoc() {
-        return Cast(this.rootDoc.presentation_targetDoc, Doc, null) || this.rootDoc;
+        return Cast(this.slideDoc.presentation_targetDoc, Doc, null) || this.slideDoc;
+    }
+
+    // computes index of this presentation slide in the presBox list
+    @computed get indexInPres() {
+        return this.presBoxView?.SlideIndex(this.slideDoc);
+    }
+
+    @computed get selectedArray() {
+        return this.presBoxView?.selectedArray;
+    }
+
+    @computed get videoRecordingIsInOverlay() {
+        return Doc.MyOverlayDocs.some(doc => doc.slides === this.slideDoc);
     }
 
     componentDidMount() {
         this.layoutDoc.layout_hideLinkButton = true;
         this._heightDisposer = reaction(
-            () => ({ expand: this.rootDoc.presentation_expandInlineButton, height: this.collapsedHeight }),
+            () => ({ expand: this.slideDoc.presentation_expandInlineButton, height: this.collapsedHeight }),
             ({ expand, height }) => (this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0)),
             { fireImmediately: true }
         );
@@ -73,31 +89,21 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this._heightDisposer?.();
     }
 
-    /**
-     * Returns a local transformed coordinate array for given coordinates.
-     */
-    ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
-
-    @action
-    presExpandDocumentClick = () => (this.rootDoc.presentation_expandInlineButton = !this.rootDoc.presentation_expandInlineButton);
-
-    embedHeight = (): number => this.collapsedHeight + this.expandViewHeight;
-    // embedWidth = () => this.props.PanelWidth();
-    // embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight);
-    embedWidth = (): number => this.props.PanelWidth() / 2;
+    presExpandDocumentClick = () => (this.slideDoc.presentation_expandInlineButton = !this.slideDoc.presentation_expandInlineButton);
+    embedHeight = () => this.collapsedHeight + this.expandViewHeight;
+    embedWidth = () => this.props.PanelWidth() / 2;
     styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
-        if (property === StyleProp.Opacity) return 1;
-        return this.props.styleProvider?.(doc, props, property);
+        return property === StyleProp.Opacity ? 1 : this.props.styleProvider?.(doc, props, property);
     };
     /**
      * The function that is responsible for rendering a preview or not for this
      * presentation element.
      */
     @computed get renderEmbeddedInline() {
-        return !this.rootDoc.presentation_expandInlineButton || !this.targetDoc ? null : (
+        return !this.slideDoc.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.slideDoc)}
                     DataDoc={undefined} //this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
@@ -121,7 +127,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     pinToPres={returnFalse}
                     bringToFront={returnFalse}
                 />
-                {/* <div className="presItem-embeddedMask" /> */}
             </div>
         );
     }
@@ -158,26 +163,22 @@ 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.slideDoc.presentation_transition) transitionInS = NumCast(this.slideDoc.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.slideDoc.presentation_movement === PresMovement.Jump || this.slideDoc.presentation_movement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
     }
 
-    private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
-    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-    private _titleRef: React.RefObject<EditableView> = React.createRef();
-
     @action
     headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         const element = e.target as any;
         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.slideDoc, 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.slideDoc, 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 +189,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     startDrag = (e: PointerEvent) => {
         const miniView: boolean = this.toolbarWidth <= 100;
-        const activeItem = this.rootDoc;
+        const activeItem = this.slideDoc;
         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.slideDoc);
         dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc;
         dragData.moveDocument = this.props.moveDocument;
         const dragItem: HTMLElement[] = [];
@@ -237,7 +238,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     onPointerMove = (e: PointerEvent) => {
-        const slide = this._itemRef.current!;
+        const slide = this._itemRef.current;
         const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentation_targetDoc);
         if (slide && dragIsPresItem) {
             const rect = slide.getBoundingClientRect();
@@ -256,35 +257,42 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     onPointerLeave = (e: any) => {
-        this._itemRef.current!.style.borderTop = '0px';
-        this._itemRef.current!.style.borderBottom = '0px';
+        const slide = this._itemRef.current;
+        if (slide) {
+            slide.style.borderTop = '0px';
+            slide.style.borderBottom = '0px';
+        }
         document.removeEventListener('pointermove', this.onPointerMove);
     };
 
     @action
     toggleProperties = () => {
         if (SettingsManager.propertiesWidth < 5) {
-            action(() => (SettingsManager.propertiesWidth = 250));
+            SettingsManager.propertiesWidth = 250;
         }
     };
 
-    removePresentationItem = undoable((e: React.MouseEvent) => {
-        e.stopPropagation();
-        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.removeAllRecordingInOverlay();
-    }, 'Remove doc from pres trail');
-
-    // set the value/title of the individual pres element
-    @undoBatch
-    @action
-    onSetValue = (value: string) => {
-        this.rootDoc.title = !value.trim().length ? '-untitled-' : value;
-        return true;
-    };
+    removePresentationItem = undoable(
+        action((e: React.MouseEvent) => {
+            e.stopPropagation();
+            if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
+                this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
+            }
+            this.props.removeDocument?.(this.slideDoc);
+            this.presBoxView?.removeFromSelectedArray(this.slideDoc);
+            this.removeAllRecordingInOverlay();
+        }),
+        'Remove doc from pres trail'
+    );
+
+    // set title of the individual pres slide
+    onSetValue = undoable(
+        action((value: string) => {
+            this.slideDoc.title = !value.trim().length ? '-untitled-' : value;
+            return true;
+        }),
+        'set title of pres element'
+    );
 
     /**
      * Method called for updating the view of the currently selected document
@@ -305,128 +313,100 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // activeItem.config_pinLayout = true;
     };
 
-    //wait i dont think i have to do anything here since by default it'll revert to the previously saved if I don't save
-    //so basically, don't have an onClick for this, just let it do nada for now
-    @undoBatch
-    @action
-    revertToPreviouslySaved = (presTargetDoc: Doc, activeItem: Doc) => {
-        const target = DocCast(activeItem.annotationOn) ?? activeItem;
-        PresBox.reversePin(activeItem, target);
-    };
-
     /**
      * Method called for updating the view of the currently selected document
      *
      * @param presTargetDoc
      * @param activeItem
      */
-    @undoBatch
-    @action
-    updateCapturedViewContents = (presTargetDoc: Doc, activeItem: Doc) => {
-        const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
-        PresBox.pinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target);
-    };
-
-    @computed get recordingIsInOverlay() {
-        return Doc.MyOverlayDocs.some(doc => doc.slides === this.rootDoc);
-    }
+    updateCapturedViewContents = undoable(
+        action((presTargetDoc: Doc, activeItem: Doc) => {
+            const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
+            PresBox.pinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target);
+        }),
+        'updated captured view contents'
+    );
 
     // a previously recorded video will have timecode defined
-    static videoIsRecorded = (activeItem: Opt<Doc>) => {
-        const casted = Cast(activeItem?.recording, Doc, null);
-        return casted && 'layout_currentTimecode' in casted;
-    };
+    static videoIsRecorded = (activeItem: Opt<Doc>) => 'layout_currentTimecode' in (DocCast(activeItem?.recording) ?? {});
 
-    removeAllRecordingInOverlay = () => {
-        Doc.MyOverlayDocs.filter(doc => doc.slides === this.rootDoc).forEach(Doc.RemFromMyOverlay);
-    };
+    removeAllRecordingInOverlay = () => Doc.MyOverlayDocs.filter(doc => doc.slides === this.slideDoc).forEach(Doc.RemFromMyOverlay);
 
+    /// remove all videos that have been recorded from overlay (leave videso that are being recorded to avoid losing data)
     static removeEveryExistingRecordingInOverlay = () => {
-        // Remove every recording that already exists in overlay view
-        Doc.MyOverlayDocs.forEach(doc => {
-            if (doc.slides !== null) {
-                // if it's a recording video, don't remove from overlay (user can lose data)
-                if (PresElementBox.videoIsRecorded(DocCast(doc.slides))) {
-                    Doc.RemFromMyOverlay(doc);
-                }
-            }
-        });
-    };
-
-    @undoBatch
-    @action
-    hideRecording = (e: React.MouseEvent, iconClick: boolean = false) => {
-        e.stopPropagation();
-        this.removeAllRecordingInOverlay();
-        // if (iconClick) PresElementBox.showVideo = false;
-    };
-
-    @undoBatch
-    @action
-    showRecording = (activeItem: Doc, iconClick: boolean = false) => {
-        // remove the overlays on switch *IF* not opened from the specific icon
-        if (!iconClick) PresElementBox.removeEveryExistingRecordingInOverlay();
-
-        if (activeItem.recording) {
-            Doc.AddToMyOverlay(DocCast(activeItem.recording));
-        }
+        Doc.MyOverlayDocs.filter(doc => doc.slides !== null && PresElementBox.videoIsRecorded(DocCast(doc.slides))) //
+            .forEach(Doc.RemFromMyOverlay);
     };
 
-    @undoBatch
-    @action
-    startRecording = (e: React.MouseEvent, activeItem: Doc) => {
-        e.stopPropagation();
-        if (PresElementBox.videoIsRecorded(activeItem)) {
-            // if we already have an existing recording
-            this.showRecording(activeItem, true);
-            //   // if we already have an existing recording
-            //   Doc.AddToMyOverlay(Cast(activeItem.recording, Doc, null));
-        } else {
-            // Remove every recording that already exists in overlay view
-            // this is a design decision to clear to focus in on the recoding mode
-            PresElementBox.removeEveryExistingRecordingInOverlay();
-
-            // if we dont have any recording
-            const recording = Docs.Create.WebCamDocument('', {
-                _width: 384,
-                _height: 216,
-                layout_hideDocumentButtonBar: true,
-                layout_hideDecorationTitle: true,
-                layout_hideOpenButton: true,
-                // hideDeleteButton: true,
-                cloneFieldFilter: new List<string>(['isSystem']),
-            });
-
-            // attach the recording to the slide, and attach the slide to the recording
-            recording.slides = activeItem;
-            activeItem.recording = recording;
-
-            // make recording box appear in the bottom right corner of the screen
-            recording.overlayX = window.innerWidth - NumCast(recording._width) - 20;
-            recording.overlayY = window.innerHeight - NumCast(recording._height) - 20;
-            Doc.AddToMyOverlay(recording);
-        }
-    };
+    hideRecording = undoable(
+        action((e: React.MouseEvent, iconClick: boolean = false) => {
+            e.stopPropagation();
+            this.removeAllRecordingInOverlay();
+        }),
+        'hide video recording'
+    );
+
+    showRecording = undoable(
+        action((activeItem: Doc, iconClick: boolean = false) => {
+            // remove the overlays on switch *IF* not opened from the specific icon
+            if (!iconClick) PresElementBox.removeEveryExistingRecordingInOverlay();
+
+            activeItem.recording && Doc.AddToMyOverlay(DocCast(activeItem.recording));
+        }),
+        'show video recording'
+    );
+
+    startRecording = undoable(
+        action((e: React.MouseEvent, activeItem: Doc) => {
+            e.stopPropagation();
+            if (PresElementBox.videoIsRecorded(activeItem)) {
+                // if we already have an existing recording
+                this.showRecording(activeItem, true);
+                //   // if we already have an existing recording
+                //   Doc.AddToMyOverlay(Cast(activeItem.recording, Doc, null));
+            } else {
+                // we dont have any recording
+                // Remove every recording that already exists in overlay view
+                // this is a design decision to clear to focus in on the recoding mode
+                PresElementBox.removeEveryExistingRecordingInOverlay();
+
+                // create and add a recording to the slide
+                // make recording box appear in the bottom right corner of the screen
+                Doc.AddToMyOverlay(
+                    (activeItem.recording = Docs.Create.WebCamDocument('', {
+                        _width: 384,
+                        _height: 216,
+                        overlayX: window.innerWidth - 384 - 20,
+                        overlayY: window.innerHeight - 216 - 20,
+                        layout_hideDocumentButtonBar: true,
+                        layout_hideDecorationTitle: true,
+                        layout_hideOpenButton: true,
+                        cloneFieldFilter: new List<string>(['isSystem']),
+                        slides: activeItem, // attach the slide to the recording
+                    }))
+                );
+            }
+        }),
+        'start video recording'
+    );
 
     @undoBatch
     @action
     lfg = (e: React.MouseEvent) => {
         e.stopPropagation();
         // TODO: fix this bug
-        const { toggleChildrenRun } = this.rootDoc;
-        TreeView.ToggleChildrenRun.get(this.rootDoc)?.();
+        const { toggleChildrenRun } = this.slideDoc;
+        TreeView.ToggleChildrenRun.get(this.slideDoc)?.();
 
-        // call this.rootDoc.recurChildren() to get all the children
+        // call this.slideDoc.recurChildren() to get all the children
         // if (iconClick) PresElementBox.showVideo = false;
     };
 
     @computed
     get toolbarWidth(): number {
         const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
-        let width: number = NumCast(this.presBox?._width);
-        if (presBoxDocView) width = presBoxDocView.props.PanelWidth();
-        if (width === 0) width = 300;
-        return width;
+        const width = NumCast(this.presBox?._width);
+        return presBoxDocView ? presBoxDocView.props.PanelWidth() : width ? width : 300;
     }
 
     @computed get presButtons() {
@@ -434,8 +414,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.slideDoc;
+        const hasChildren = BoolCast(this.slideDoc?.hasChildren);
 
         const items: JSX.Element[] = [];
 
@@ -460,9 +440,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </Tooltip>
         );
         items.push(
-            <Tooltip key="slash" title={<div className="dash-tooltip">{this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
-                <div className="slideButton" onClick={e => (this.recordingIsInOverlay ? this.hideRecording(e, true) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}>
-                    <FontAwesomeIcon icon={`video${this.recordingIsInOverlay ? '-slash' : ''}`} onPointerDown={e => e.stopPropagation()} />
+            <Tooltip key="slash" title={<div className="dash-tooltip">{this.videoRecordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
+                <div className="slideButton" onClick={e => (this.videoRecordingIsInOverlay ? this.hideRecording(e, true) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}>
+                    <FontAwesomeIcon icon={`video${this.videoRecordingIsInOverlay ? '-slash' : ''}`} onPointerDown={e => e.stopPropagation()} />
                 </div>
             </Tooltip>
         );
@@ -498,14 +478,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.slideDoc.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.slideDoc.presentation_expandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
                 </div>
             </Tooltip>
         );
@@ -536,18 +516,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.slideDoc) ? 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.slideDoc;
 
         return (
             <div
                 className="presItem-container"
-                key={this.rootDoc[Id] + this.indexInPres}
+                key={this.slideDoc[Id] + this.indexInPres}
                 ref={this._itemRef}
                 style={{
                     backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
@@ -559,7 +539,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.slideDoc, this._itemRef.current!, this._dragRef.current!, false);
                 })}
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
@@ -571,7 +551,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.slideDoc.runProcess ? ' testingv2' : ''}`}
                         style={{
                             display: 'infline-block',
                             backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
@@ -583,7 +563,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.slideDoc.presentation_expandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
                                 cursor: isSelected ? 'text' : 'grab',
                             }}>
                             <div
@@ -611,6 +591,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     render() {
-        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
+        return !(this.slideDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 9b9f54a43793ca6ffb26c56f962d11ba8325abd2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 29 Dec 2023 17:01:40 -0500
Subject: cleaned up imports, mobx observable initialization and some compile
 errors.

---
 src/Utils.ts                                       |  27 ++---
 src/client/util/CalendarManager.tsx                |  34 +++---
 src/client/util/CaptureManager.tsx                 |   2 +-
 src/client/util/CurrentUserUtils.ts                |  14 +--
 src/client/util/GroupManager.tsx                   |   2 +-
 src/client/util/HypothesisUtils.ts                 |  13 ++-
 src/client/views/DocumentButtonBar.tsx             |   5 -
 src/client/views/DocumentDecorations.tsx           |  11 +-
 src/client/views/FilterPanel.tsx                   |  11 +-
 src/client/views/GestureOverlay.tsx                |  25 +++--
 src/client/views/InkTranscription.tsx              |   8 +-
 src/client/views/InkingStroke.tsx                  |  76 +++++---------
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/MetadataEntryMenu.tsx             |  12 +--
 src/client/views/PreviewCursor.tsx                 |   2 +-
 src/client/views/PropertiesButtons.tsx             |  17 +--
 .../views/PropertiesDocBacklinksSelector.tsx       |   4 +-
 src/client/views/PropertiesSection.tsx             |   6 +-
 src/client/views/TemplateMenu.tsx                  |   2 +-
 src/client/views/UndoStack.tsx                     |  22 ++--
 src/client/views/animationtimeline/Region.tsx      |   2 +-
 src/client/views/animationtimeline/Timeline.tsx    |   4 +-
 .../views/animationtimeline/TimelineOverview.tsx   |   2 +-
 src/client/views/animationtimeline/Track.tsx       |   8 +-
 .../views/collections/CollectionCalendarView.tsx   |  19 ++--
 .../views/collections/CollectionDockingView.tsx    |   4 +-
 src/client/views/collections/CollectionMenu.tsx    |   3 -
 .../CollectionNoteTakingViewDivider.tsx            |   2 +-
 .../views/collections/CollectionPileView.tsx       |   2 +-
 .../collections/CollectionStackedTimeline.tsx      |   4 +-
 .../CollectionStackingViewFieldColumn.tsx          |   2 +-
 src/client/views/collections/CollectionSubView.tsx |   8 +-
 .../views/collections/CollectionTimeView.tsx       |  14 +--
 src/client/views/collections/CollectionView.tsx    |   2 +-
 src/client/views/collections/KeyRestrictionRow.tsx |  37 +++----
 src/client/views/collections/TreeView.tsx          |   2 +-
 .../CollectionFreeFormBackgroundGrid.tsx           |   2 +-
 .../CollectionFreeFormInfoState.tsx                |   2 +-
 .../CollectionFreeFormInfoUI.tsx                   |   8 +-
 .../CollectionFreeFormLinksView.tsx                |   4 +-
 .../CollectionFreeFormPannableContents.tsx         |   5 +-
 .../CollectionFreeFormRemoteCursors.tsx            |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   5 +-
 .../collectionGrid/CollectionGridView.tsx          |   4 +-
 .../views/collections/collectionGrid/Grid.tsx      |   2 +-
 .../CollectionMulticolumnView.tsx                  |   4 +-
 .../CollectionMultirowView.tsx                     |   3 +-
 .../collectionMulticolumn/MulticolumnResizer.tsx   |   8 +-
 .../MulticolumnWidthLabel.tsx                      |  27 +++--
 .../collectionMulticolumn/MultirowHeightLabel.tsx  |  22 ++--
 .../collectionMulticolumn/MultirowResizer.tsx      |   6 +-
 .../collectionSchema/SchemaColumnHeader.tsx        |   4 +-
 src/client/views/linking/LinkMenuItem.tsx          |   6 +-
 src/client/views/linking/LinkPopup.tsx             |   8 +-
 .../views/linking/LinkRelationshipSearch.tsx       |   2 +-
 .../views/newlightbox/ButtonMenu/ButtonMenu.tsx    |   4 +-
 src/client/views/newlightbox/NewLightboxView.tsx   |  26 ++---
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |   4 +-
 .../views/nodes/DataVizBox/SchemaCSVPopUp.tsx      |  30 +++---
 .../nodes/DataVizBox/components/Histogram.tsx      |   6 +-
 .../nodes/DataVizBox/components/LineChart.tsx      |  28 ++---
 .../views/nodes/DataVizBox/components/PieChart.tsx |   6 +-
 .../views/nodes/DataVizBox/components/TableBox.tsx |  12 +--
 src/client/views/nodes/DocumentContentsView.tsx    |   6 +-
 src/client/views/nodes/DocumentView.tsx            |   7 +-
 src/client/views/nodes/FaceRectangle.tsx           |   4 +-
 src/client/views/nodes/FaceRectangles.tsx          |   4 +-
 src/client/views/nodes/FieldView.tsx               |   2 +-
 src/client/views/nodes/KeyValuePair.tsx            |   6 +-
 src/client/views/nodes/LinkDescriptionPopup.tsx    |   2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   8 +-
 src/client/views/nodes/LoadingBox.tsx              |   2 +-
 src/client/views/nodes/MapBox/AnimationUtility.ts  |  13 +--
 .../views/nodes/MapBox/DirectionsAnchorMenu.tsx    |  14 +--
 src/client/views/nodes/MapBox/MapAnchorMenu.tsx    | 106 +++++++------------
 src/client/views/nodes/MapBox/MapBox.tsx           |  60 +++++------
 src/client/views/nodes/MapBox/MapBox2.tsx          |   2 +-
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |  20 ++--
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 .../nodes/PhysicsBox/PhysicsSimulationBox.tsx      |   4 +-
 .../nodes/PhysicsBox/PhysicsSimulationWeight.tsx   |   2 +-
 src/client/views/nodes/RadialMenu.tsx              |   2 +-
 src/client/views/nodes/RadialMenuItem.tsx          |   2 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   4 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   2 +-
 src/client/views/nodes/ScriptingBox.tsx            |   8 +-
 src/client/views/nodes/TaskCompletedBox.tsx        |   8 +-
 src/client/views/nodes/VideoBox.tsx                |   6 +-
 src/client/views/nodes/audio/AudioWaveform.tsx     |   2 +-
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 114 +++++++++------------
 .../views/nodes/formattedText/DashDocView.tsx      |   6 +-
 .../views/nodes/formattedText/EquationView.tsx     |   7 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   4 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |   2 +-
 .../views/nodes/importBox/ImportElementBox.tsx     |   8 +-
 src/client/views/nodes/trails/PresBox.tsx          |   6 +-
 src/client/views/pdf/Annotation.tsx                |   5 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |  16 +--
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 src/client/views/search/SearchBox.tsx              |  15 ++-
 src/client/views/selectedDoc/SelectedDocView.tsx   |   6 +-
 src/client/views/topbar/TopBar.tsx                 |   4 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |   5 +-
 src/debug/Repl.tsx                                 |  10 +-
 src/debug/Viewer.tsx                               |  18 ++--
 src/fields/CursorField.ts                          |   7 +-
 src/fields/Doc.ts                                  |   4 +-
 src/fields/List.ts                                 |   8 +-
 src/fields/Proxy.ts                                |  12 +--
 src/fields/util.ts                                 |   4 +-
 src/mobile/ImageUpload.tsx                         |   6 +-
 src/mobile/MobileInkOverlay.tsx                    |   2 +-
 src/mobile/MobileInterface.tsx                     | 110 ++++++++++----------
 src/typings/index.d.ts                             |   4 +-
 test/test.ts                                       | 106 ++++++++++---------
 116 files changed, 650 insertions(+), 793 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index a060e4a2c..b5c218e01 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,13 +1,12 @@
-import * as uuid from 'uuid';
 import { ColorResult } from 'react-color';
+import * as uuid from 'uuid';
 //import { Socket } from '../node_modules/socket.io-client';
-import { Socket } from '../node_modules/socket.io/dist/index';
+import * as Color from 'color';
 import * as rp from 'request-promise';
+import { Socket } from '../node_modules/socket.io/dist/index';
 import { DocumentType } from './client/documents/DocumentTypes';
 import { Colors } from './client/views/global/globalEnums';
 import { Message } from './server/Message';
-import * as Color from 'color';
-import { action } from 'mobx';
 
 export namespace Utils {
     export let CLICK_TIME = 300;
@@ -896,23 +895,19 @@ export function setupMoveUpEvents(
     document.addEventListener('click', _clickEvent, true);
 }
 
-export function dateRangeStrToDates (dateStr: string) {
+export function dateRangeStrToDates(dateStr: string) {
     // dateStr in yyyy-mm-dd format
-    const dateRangeParts = dateStr.split("|"); // splits into from and to date
-    const fromParts = dateRangeParts[0].split("-");
-    const toParts = dateRangeParts[1].split("-");
+    const dateRangeParts = dateStr.split('|'); // splits into from and to date
+    const fromParts = dateRangeParts[0].split('-');
+    const toParts = dateRangeParts[1].split('-');
 
     const fromYear = parseInt(fromParts[0]);
-    const fromMonth = parseInt(fromParts[1])-1;
+    const fromMonth = parseInt(fromParts[1]) - 1;
     const fromDay = parseInt(fromParts[2]);
 
     const toYear = parseInt(toParts[0]);
-    const toMonth = parseInt(toParts[1])-1;
+    const toMonth = parseInt(toParts[1]) - 1;
     const toDay = parseInt(toParts[2]);
 
-
-    return [
-        new Date(fromYear, fromMonth, fromDay),
-        new Date(toYear, toMonth, toDay)
-    ];
-}
\ No newline at end of file
+    return [new Date(fromYear, fromMonth, fromDay), new Date(toYear, toMonth, toDay)];
+}
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
index 6ef2d3429..6e9094b3a 100644
--- a/src/client/util/CalendarManager.tsx
+++ b/src/client/util/CalendarManager.tsx
@@ -1,26 +1,24 @@
-import * as React from 'react';
-import './CalendarManager.scss';
+import { TextField } from '@mui/material';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { action, computed, observable, runInAction, makeObservable } from 'mobx';
+import * as React from 'react';
+import Select from 'react-select';
 import { Doc, DocListCast } from '../../fields/Doc';
-import { DocumentView } from '../views/nodes/DocumentView';
+import { DocData } from '../../fields/DocSymbols';
+import { StrCast } from '../../fields/Types';
 import { DictationOverlay } from '../views/DictationOverlay';
-import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
 import { MainViewModal } from '../views/MainViewModal';
-import { TextField } from '@mui/material';
-import Select from 'react-select';
-import { SettingsManager } from './SettingsManager';
-import { DateCast, DocCast, StrCast } from '../../fields/Types';
-import { SelectionManager } from './SelectionManager';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import './CalendarManager.scss';
 import { DocumentManager } from './DocumentManager';
-import { DocData } from '../../fields/DocSymbols';
+import { SelectionManager } from './SelectionManager';
+import { SettingsManager } from './SettingsManager';
 // import { DateRange, Range, RangeKeyDict } from 'react-date-range';
-import { Button } from 'browndash-components';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Provider, defaultTheme } from '@adobe/react-spectrum';
-import { DateValue } from '@internationalized/date';
-import { DateRangePicker, SpectrumDateRangePickerProps } from '@adobe/react-spectrum';
+import { DateRangePicker, Provider, defaultTheme } from '@adobe/react-spectrum';
 import { IconLookup, faPlus } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button } from 'browndash-components';
 import { Docs } from '../documents/Documents';
 import { ObservableReactComponent } from '../views/ObservableReactComponent';
 // import 'react-date-range/dist/styles.css';
@@ -51,8 +49,8 @@ const formatCalendarDateToString = (calendarDate: any) => {
 export class CalendarManager extends ObservableReactComponent<{}> {
     public static Instance: CalendarManager;
     @observable private isOpen = false;
-    @observable private targetDoc: Doc | undefined; // the target document
-    @observable private targetDocView: DocumentView | undefined; // the DocumentView of the target doc
+    @observable private targetDoc: Doc | undefined = undefined; // the target document
+    @observable private targetDocView: DocumentView | undefined = undefined; // the DocumentView of the target doc
     @observable private dialogueBoxOpacity = 1; // for the modal
     @observable private overlayOpacity = 0.4; // for the modal
 
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 071fc1ee9..c1e0a5b2e 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -15,7 +15,7 @@ import { SelectionManager } from './SelectionManager';
 export class CaptureManager extends React.Component<{}> {
     public static Instance: CaptureManager;
     static _settingsStyle = addStyleSheet();
-    @observable _document: any;
+    @observable _document: any = undefined;
     @observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not.
 
     constructor(props: {}) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c1d74c398..3f28f44fc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,5 +1,6 @@
 import { observable, reaction, runInAction } from "mobx";
 import * as rp from 'request-promise';
+import { OmitKeys, Utils } from "../../Utils";
 import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
 import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
@@ -8,29 +9,28 @@ import { RichTextField } from "../../fields/RichTextField";
 import { listSpec } from "../../fields/Schema";
 import { ScriptField } from "../../fields/ScriptField";
 import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types";
-import { nullAudio, WebField } from "../../fields/URLField";
+import { WebField, nullAudio } from "../../fields/URLField";
 import { SetCachedGroups, SharingPermissions } from "../../fields/util";
 import { GestureUtils } from "../../pen-gestures/GestureUtils";
-import { OmitKeys, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
-import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
 import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
-import { TreeViewType } from "../views/collections/CollectionTreeView";
+import { DocUtils, Docs, DocumentOptions, FInfo } from "../documents/Documents";
 import { DashboardView } from "../views/DashboardView";
+import { OverlayView } from "../views/OverlayView";
+import { TreeViewType } from "../views/collections/CollectionTreeView";
 import { Colors } from "../views/global/globalEnums";
 import { media_state } from "../views/nodes/AudioBox";
-import { DocumentView, OpenWhere } from "../views/nodes/DocumentView";
+import { OpenWhere } from "../views/nodes/DocumentView";
 import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
 import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
-import { OverlayView } from "../views/OverlayView";
 import { DragManager, dropActionType } from "./DragManager";
 import { MakeTemplate } from "./DropConverter";
 import { FollowLinkScript } from "./LinkFollower";
 import { LinkManager } from "./LinkManager";
 import { ScriptingGlobals } from "./ScriptingGlobals";
 import { ColorScheme } from "./SettingsManager";
-import { UndoManager } from "./UndoManager";
 import { SnappingManager } from "./SnappingManager";
+import { UndoManager } from "./UndoManager";
 
 interface Button {
     // DocumentOptions fields a button can set
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 90f65b648..f4f879208 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -33,7 +33,7 @@ export class GroupManager extends ObservableReactComponent<{}> {
     @observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
     @observable private users: string[] = []; // list of users populated from the database.
     @observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown.
-    @observable currentGroup: Opt<Doc>; // the currently selected group.
+    @observable currentGroup: Opt<Doc> = undefined; // the currently selected group.
     @observable private createGroupModalOpen: boolean = false;
     private inputRef: React.RefObject<HTMLInputElement> = React.createRef(); // the ref for the input box.
     private createGroupButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // the ref for the group creation button
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 990798ed3..0ae23d793 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -1,16 +1,15 @@
-import { StrCast, Cast } from '../../fields/Types';
-import { SearchUtil } from './SearchUtil';
 import { action, runInAction } from 'mobx';
+import { simulateMouseClick } from '../../Utils';
 import { Doc, Opt } from '../../fields/Doc';
+import { Cast, StrCast } from '../../fields/Types';
+import { WebField } from '../../fields/URLField';
 import { DocumentType } from '../documents/DocumentTypes';
 import { Docs } from '../documents/Documents';
-import { SelectionManager } from './SelectionManager';
-import { WebField } from '../../fields/URLField';
-import { DocumentManager } from './DocumentManager';
 import { DocumentLinksButton } from '../views/nodes/DocumentLinksButton';
-import { simulateMouseClick, Utils } from '../../Utils';
 import { DocumentView } from '../views/nodes/DocumentView';
-import { Id } from '../../fields/FieldSymbols';
+import { DocumentManager } from './DocumentManager';
+import { SearchUtil } from './SearchUtil';
+import { SelectionManager } from './SelectionManager';
 
 export namespace Hypothesis {
     /**
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index f0887676f..3938b81cd 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -40,14 +40,9 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
     private _pullAnimating = false;
     private _pushAnimating = false;
     private _pullColorAnimating = false;
-    @observable private pushIcon: IconProp = 'arrow-alt-circle-up';
-    @observable private pullIcon: IconProp = 'arrow-alt-circle-down';
-    @observable private pullColor: string = 'white';
     @observable public isAnimatingFetch = false;
     @observable public isAnimatingPulse = false;
 
-    @observable private openHover: UtilityButtonState = UtilityButtonState.Default;
-
     @observable public static Instance: DocumentButtonBar;
     public static hasPushedHack = false;
     public static hasPulledHack = false;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d2f23c2f3..dab6e6193 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -12,7 +12,7 @@ import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
 import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
+import { emptyFunction, lightOrDark, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
@@ -85,7 +85,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     }
 
     @computed get ClippedBounds() {
-        const bounds = this.Bounds;
+        const bounds = { ...this.Bounds };
         const leftBounds = this._props.boundsLeft;
         const topBounds = LightboxView.LightboxDoc ? 0 : this._props.boundsTop;
         bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
@@ -106,9 +106,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                             : { x: Math.min(rect.left, bounds.x),
                                 y: Math.min(rect.top, bounds.y),
                                 r: Math.max(rect.right, bounds.r),
-                                b: Math.max(rect.bottom, bounds.b),
-                                c: SelectionManager.Views.length === 1 ? rect.center : undefined },
-                    { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as { X: number; Y: number } | undefined }); // prettier-ignore
+                                b: Math.max(rect.bottom, bounds.b)},
+                    { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); // prettier-ignore
     }
 
     @action
@@ -811,7 +810,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
                                 <div
                                     className="documentDecorations-borderRadius"
                                     style={{
-                                        background: `${this._isRounding ? Colors.MEDIUM_BLUE : SettingsManager.userColor}`,
+                                        background: `${this._isRounding ? Colors.MEDIUM_BLUE : lightOrDark(StrCast(seldocview.layoutDoc._backgroundColor, SettingsManager.userColor))}`,
                                         transform: `translate(${radiusHandleLocation ?? 0}px, ${(radiusHandleLocation ?? 0) + (this._showNothing ? 0 : this._titleHeight)}px)`,
                                     }}
                                     onPointerDown={this.onRadiusDown}
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index a3c4350d9..d3075a5cd 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -1,24 +1,23 @@
-import * as React from 'react';
 import { action, computed, observable, ObservableMap } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
 import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai';
 import { CiCircleRemove } from 'react-icons/ci';
 import Select from 'react-select';
 import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
-import { DocOptions, DocumentOptions, FInfo } from '../documents/Documents';
+import { DocOptions, FInfo } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
 import { UserOptions } from '../util/GroupManager';
 import { SearchUtil } from '../util/SearchUtil';
+import { SettingsManager } from '../util/SettingsManager';
 import { undoable } from '../util/UndoManager';
 import './FilterPanel.scss';
 import { FieldView } from './nodes/FieldView';
 import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components';
-import { SettingsManager } from '../util/SettingsManager';
-import { Id } from '../../fields/FieldSymbols';
-import { List } from '../../fields/List';
-import { emptyFunction } from '../../Utils';
 
 interface filterProps {
     Document: Doc;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 33cda6a62..65aadc148 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1,15 +1,14 @@
-import * as React from 'react';
 import * as fitCurve from 'fit-curve';
-import { action, computed, makeObservable, observable, runInAction, toJS } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import * as React from 'react';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { Doc, Opt } from '../../fields/Doc';
 import { InkData, InkTool } from '../../fields/InkField';
 import { BoolCast, NumCast } from '../../fields/Types';
 import MobileInkOverlay from '../../mobile/MobileInkOverlay';
 import { GestureUtils } from '../../pen-gestures/GestureUtils';
 import { MobileInkOverlayContent } from '../../server/Message';
-import { CognitiveServices } from '../cognitive_services/CognitiveServices';
 import { InteractionUtils } from '../util/InteractionUtils';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { Transform } from '../util/Transform';
@@ -29,9 +28,9 @@ import {
     SetActiveInkColor,
     SetActiveInkWidth,
 } from './InkingStroke';
+import { ObservableReactComponent } from './ObservableReactComponent';
 import { checkInksToGroup } from './global/globalScripts';
 import { DocumentView } from './nodes/DocumentView';
-import { ObservableReactComponent } from './ObservableReactComponent';
 
 interface GestureOverlayProps {
     isActive: boolean;
@@ -48,22 +47,22 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
         return BoolCast(Doc.UserDoc().recognizeGestures);
     }
 
-    @observable public InkShape: Opt<GestureUtils.Gestures>;
-    @observable public SavedColor?: string;
-    @observable public SavedWidth?: number;
+    @observable public InkShape: Opt<GestureUtils.Gestures> = undefined;
+    @observable public SavedColor?: string = undefined;
+    @observable public SavedWidth?: number = undefined;
     @observable public Tool: ToolglassTools = ToolglassTools.None;
     @observable public KeepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
 
-    @observable private _thumbX?: number;
-    @observable private _thumbY?: number;
+    @observable private _thumbX?: number = undefined;
+    @observable private _thumbY?: number = undefined;
     @observable private _selectedIndex: number = -1;
     @observable private _menuX: number = -300;
     @observable private _menuY: number = -300;
-    @observable private _pointerY?: number;
+    @observable private _pointerY?: number = undefined;
     @observable private _points: { X: number; Y: number }[] = [];
     @observable private _strokes: InkData[] = [];
-    @observable private _palette?: JSX.Element;
-    @observable private _clipboardDoc?: JSX.Element;
+    @observable private _palette?: JSX.Element = undefined;
+    @observable private _clipboardDoc?: JSX.Element = undefined;
     @observable private _possibilities: JSX.Element[] = [];
 
     public static DownDocView: DocumentView | undefined;
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index b2ac208ca..7e2b334af 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -17,10 +17,10 @@
 // export class InkTranscription extends React.Component {
 //     static Instance: InkTranscription;
 
-//     @observable _mathRegister: any;
-//     @observable _mathRef: any;
-//     @observable _textRegister: any;
-//     @observable _textRef: any;
+//     @observable _mathRegister: any= undefined;
+//     @observable _mathRef: any= undefined;
+//     @observable _textRegister: any= undefined;
+//     @observable _textRef: any= undefined;
 //     private lastJiix: any;
 //     private currGroup?: Doc;
 
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 9c96b4563..95c677845 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -20,9 +20,9 @@
     
     Most of the operations that can be performed on an InkStroke (eg delete a point, rotate, stretch) are implemented in the InkStrokeProperties helper class
 */
-import * as React from 'react';
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc } from '../../fields/Doc';
 import { InkData, InkField } from '../../fields/InkField';
 import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
@@ -32,7 +32,6 @@ import { CognitiveServices } from '../cognitive_services/CognitiveServices';
 import { Docs } from '../documents/Documents';
 import { InteractionUtils } from '../util/InteractionUtils';
 import { SnappingManager } from '../util/SnappingManager';
-import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { ContextMenu } from './ContextMenu';
 import { ViewBoxBaseComponent } from './DocComponent';
@@ -59,14 +58,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     private _handledClick = false; // flag denoting whether ink stroke has handled a psuedo-click onPointerUp so that the real onClick event can be stopPropagated
     private _disposers: { [key: string]: IReactionDisposer } = {};
 
-    @observable _nearestSeg?: number; // nearest Bezier segment along the ink stroke to the cursor (used for displaying the Add Point highlight)
-    @observable _nearestT?: number; // nearest t value within the nearest Bezier segment "
+    @observable _nearestSeg?: number = undefined; // nearest Bezier segment along the ink stroke to the cursor (used for displaying the Add Point highlight)
+    @observable _nearestT?: number = undefined; // nearest t value within the nearest Bezier segment "
     @observable _nearestScrPt?: { X: number; Y: number }; // nearst screen point on the ink stroke ""
 
     componentDidMount() {
-        this.props.setContentView?.(this);
+        this._props.setContentView?.(this);
         this._disposers.selfDisper = reaction(
-            () => this.props.isSelected(), // react to stroke being deselected by turning off ink handles
+            () => this._props.isSelected(), // react to stroke being deselected by turning off ink handles
             selected => !selected && (InkStrokeProperties.Instance._controlButton = false)
         );
     }
@@ -76,7 +75,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
-    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.NativeDimScaling?.() || 1);
+    screenToLocal = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const subAnchor = this._subContentView?.getAnchor?.(addAsAnnotation);
@@ -100,27 +99,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.Document;
     };
 
-    /**
-     * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space);
-     * DocumentDecorations calls getBounds() on DocumentViews which call getCenter() if defined - in the case of ink it needs to be defined since
-     * the center of the ink stroke changes as the stroke is rotated.
-     */
-    getCenter = (xf: Transform) => {
-        const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
-        const angle = -NumCast(this.layoutDoc.rotation);
-        const newPoints = inkData.map(pt => {
-            const newX = Math.cos(angle) * pt.X - (Math.sin(angle) * pt.Y * inkScaleY) / inkScaleX;
-            const newY = (Math.sin(angle) * pt.X * inkScaleX) / inkScaleY + Math.cos(angle) * pt.Y;
-            return { X: newX, Y: newY };
-        });
-        const crx = (Math.max(...newPoints.map(np => np.X)) + Math.min(...newPoints.map(np => np.X))) / 2;
-        const cry = (Math.max(...newPoints.map(np => np.Y)) + Math.min(...newPoints.map(np => np.Y))) / 2;
-        const cx = Math.cos(-angle) * crx - (Math.sin(-angle) * cry * inkScaleY) / inkScaleX;
-        const cy = (Math.sin(-angle) * crx * inkScaleX) / inkScaleY + Math.cos(-angle) * cry;
-        const tc = xf.transformPoint((cx - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (cy - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2);
-        return { X: tc[0], Y: tc[1] };
-    };
-
     /**
      * analyzes the ink stroke and saves the analysis of the stroke to the 'inkAnalysis' field,
      * and the recognized words to the 'handwriting'
@@ -147,7 +125,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     onPointerDown = (e: React.PointerEvent) => {
         this._handledClick = false;
-        const inkView = this.props.docViewPath().lastElement();
+        const inkView = this._props.docViewPath().lastElement();
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const screenPts = inkData
             .map(point =>
@@ -159,7 +137,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const { nearestSeg } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
         const controlIndex = nearestSeg;
         const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex;
-        const isEditing = InkStrokeProperties.Instance._controlButton && this.props.isSelected();
+        const isEditing = InkStrokeProperties.Instance._controlButton && this._props.isSelected();
         this.controlUndo = undefined;
         setupMoveUpEvents(
             this,
@@ -187,7 +165,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     InkStrokeProperties.Instance._currentPoint = -1;
                     this._handledClick = true; // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
                     if (isEditing) {
-                        this._nearestT && this._nearestSeg !== undefined && InkStrokeProperties.Instance.addPoints(this.props.docViewPath().lastElement(), this._nearestT, this._nearestSeg, this.inkScaledData().inkData.slice());
+                        this._nearestT && this._nearestSeg !== undefined && InkStrokeProperties.Instance.addPoints(this._props.docViewPath().lastElement(), this._nearestT, this._nearestSeg, this.inkScaledData().inkData.slice());
                     }
                 }
             }),
@@ -258,8 +236,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkLeft,
             inkWidth,
             inkHeight,
-            inkScaleX: (this.props.PanelWidth() - inkStrokeWidth) / (inkWidth - inkStrokeWidth || 1) || 1,
-            inkScaleY: (this.props.PanelHeight() - inkStrokeWidth) / (inkHeight - inkStrokeWidth || 1) || 1,
+            inkScaleX: (this._props.PanelWidth() - inkStrokeWidth) / (inkWidth - inkStrokeWidth || 1) || 1,
+            inkScaleY: (this._props.PanelHeight() - inkStrokeWidth) / (inkHeight - inkStrokeWidth || 1) || 1,
         };
     };
 
@@ -311,7 +289,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      * @returns the JSX controls for displaying an editing UI for the stroke (control point & tangent handles)
      */
     componentUI = (boundsLeft: number, boundsTop: number) => {
-        const inkDoc = this.props.Document;
+        const inkDoc = this.Document;
         const { inkData, inkStrokeWidth } = this.inkScaledData();
         const screenSpaceCenterlineStrokeWidth = Math.min(3, inkStrokeWidth * this.screenToLocal().inverse().Scale); // the width of the blue line widget that shows the centerline of the ink stroke
 
@@ -321,7 +299,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const endMarker = StrCast(this.layoutDoc.stroke_endMarker);
         const markerScale = NumCast(this.layoutDoc.stroke_markerScale);
         return SnappingManager.IsDragging ? null : !InkStrokeProperties.Instance._controlButton ? (
-            !this.props.isSelected() || InkingStroke.IsClosed(inkData) ? null : (
+            !this._props.isSelected() || InkingStroke.IsClosed(inkData) ? null : (
                 <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
                     <InkEndPtHandles inkView={this} inkDoc={inkDoc} startPt={this.startPt} endPt={this.endPt} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
                 </div>
@@ -360,12 +338,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
     @computed get fillColor() {
         const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
-        return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FillColor) ?? 'transparent';
+        return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FillColor) ?? 'transparent';
     }
     @computed get strokeColor() {
         const { inkData } = this.inkScaledData();
         const fillColor = this.fillColor;
-        return !InkingStroke.IsClosed(inkData) && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
+        return !InkingStroke.IsClosed(inkData) && fillColor && fillColor !== 'transparent' ? fillColor : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
     }
     render() {
         TraceMobx();
@@ -385,9 +363,9 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 this.layoutDoc._height = Math.round(NumCast(this.layoutDoc._height));
             });
         }
-        const highlight = !this.controlUndo && this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Highlighting);
+        const highlight = !this.controlUndo && this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Highlighting);
         const highlightIndex = highlight?.highlightIndex;
-        const highlightColor = !this.props.isSelected() && !isInkMask && highlight?.highlightIndex ? highlight?.highlightColor : undefined;
+        const highlightColor = !this._props.isSelected() && !isInkMask && highlight?.highlightIndex ? highlight?.highlightColor : undefined;
         const color = StrCast(this.layoutDoc.stroke_outlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent');
 
         // Visually renders the polygonal line made by the user.
@@ -437,7 +415,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 inkScaleX,
                 inkScaleY,
                 '',
-                this.props.pointerEvents?.() ?? 'visiblepainted',
+                this._props.pointerEvents?.() ?? 'visiblepainted',
                 0.0,
                 false,
                 downHdlr,
@@ -450,7 +428,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const lineHeightGuess = +getComputedStyle(document.body).lineHeight.replace('px', '') / +getComputedStyle(document.body).fontSize.replace('px', '');
         const interactions = {
             onPointerLeave: action(() => (this._nearestScrPt = undefined)),
-            onPointerMove: this.props.isSelected() ? this.onPointerMove : undefined,
+            onPointerMove: this._props.isSelected() ? this.onPointerMove : undefined,
             onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(),
             onContextMenu: () => {
                 const cm = ContextMenu.Instance;
@@ -464,27 +442,27 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 <svg
                     className="inkStroke"
                     style={{
-                        transform: isInkMask ? `rotate(-${NumCast(this.props.CollectionFreeFormDocumentView?.()._props.w_Rotation?.() ?? 0)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                        transform: isInkMask ? `rotate(-${NumCast(this._props.CollectionFreeFormDocumentView?.()._props.w_Rotation?.() ?? 0)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
                         // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
-                        cursor: this.props.isSelected() ? 'default' : undefined,
+                        cursor: this._props.isSelected() ? 'default' : undefined,
                     }}
                     {...interactions}>
                     {clickableLine(this.onPointerDown, isInkMask)}
                     {isInkMask ? null : inkLine}
                 </svg>
-                {!closed || (!RTFCast(this.dataDoc.text)?.Text && (!this.props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
+                {!closed || (!RTFCast(this.dataDoc.text)?.Text && (!this._props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
                     <div
                         className="inkStroke-text"
                         style={{
                             color: StrCast(this.layoutDoc.textColor, 'black'),
-                            pointerEvents: this.props.isDocumentActive?.() ? 'all' : undefined,
+                            pointerEvents: this._props.isDocumentActive?.() ? 'all' : undefined,
                             width: NumCast(this.layoutDoc._width),
-                            transform: `scale(${this.props.NativeDimScaling?.() || 1})`,
+                            transform: `scale(${this._props.NativeDimScaling?.() || 1})`,
                             transformOrigin: 'top left',
-                            //top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.NativeDimScaling?.() || 1)) / 2,
+                            //top: (this._props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this._props.NativeDimScaling?.() || 1)) / 2,
                         }}>
                         <FormattedTextBox
-                            {...this.props}
+                            {...this._props}
                             setHeight={undefined}
                             setContentView={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
                             yPadding={10}
@@ -493,7 +471,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                             dontRegisterView={true}
                             noSidebar={true}
                             dontScale={true}
-                            isContentActive={this.props.isContentActive}
+                            isContentActive={this._props.isContentActive}
                         />
                     </div>
                 )}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 094bd2adb..35ffab337 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -118,7 +118,7 @@ export class MainView extends ObservableReactComponent<{}> {
     @computed private get userDoc() {
         return Doc.UserDoc();
     }
-    @observable mainDoc: Opt<Doc>;
+    @observable mainDoc: Opt<Doc> = undefined;
     @computed private get mainContainer() {
         if (window.location.pathname.startsWith('/doc/') && Doc.CurrentUserEmail === 'guest') {
             DocServer.GetRefField(window.location.pathname.substring('/doc/'.length)).then(main => runInAction(() => (this.mainDoc = main as Doc)));
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 5c6912121..89c3c41f8 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -1,12 +1,12 @@
-import * as React from 'react';
-import './MetadataEntryMenu.scss';
+import { IReactionDisposer, action, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
-import { KeyValueBox } from './nodes/KeyValueBox';
-import { Doc, Field, DocListCastAsync, DocListCast } from '../../fields/Doc';
+import * as React from 'react';
 import * as Autosuggest from 'react-autosuggest';
-import { undoBatch, UndoManager } from '../util/UndoManager';
 import { emptyFunction, emptyPath } from '../../Utils';
+import { Doc, DocListCast, Field } from '../../fields/Doc';
+import { undoBatch } from '../util/UndoManager';
+import './MetadataEntryMenu.scss';
+import { KeyValueBox } from './nodes/KeyValueBox';
 
 export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
 export interface MetadataEntryProps {
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 166afc0c2..a69a5f5e8 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -24,7 +24,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
     _addLiveTextDoc?: (doc: Doc) => void;
     _nudge?: undefined | ((x: number, y: number) => boolean);
     _slowLoadDocuments?: (files: File[] | string, options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => Promise<void>;
-    @observable _clickPoint: number[];
+    @observable _clickPoint: number[] = [];
     @observable public Visible = false;
     public Doc: Opt<Doc>;
     constructor(props: any) {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 8540e81e1..f1dadcd5e 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -2,6 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { AiOutlineColumnWidth } from 'react-icons/ai';
 import { BiHide, BiShow } from 'react-icons/bi';
 import { BsGrid3X3GapFill } from 'react-icons/bs';
@@ -12,28 +13,20 @@ import { RxWidth } from 'react-icons/rx';
 import { TbEditCircle, TbEditCircleOff, TbHandOff, TbHandStop, TbHighlight, TbHighlightOff } from 'react-icons/tb';
 import { TfiBarChart } from 'react-icons/tfi';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { BoolCast, ScriptCast } from '../../fields/Types';
 import { ImageField } from '../../fields/URLField';
-import { DocUtils } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocUtils } from '../documents/Documents';
 import { IsFollowLinkScript } from '../util/LinkFollower';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SettingsManager } from '../util/SettingsManager';
-import { undoable, undoBatch } from '../util/UndoManager';
-import { Colors } from './global/globalEnums';
+import { undoBatch, undoable } from '../util/UndoManager';
 import { InkingStroke } from './InkingStroke';
-import { DocumentView, OpenWhere } from './nodes/DocumentView';
 import './PropertiesButtons.scss';
-import * as React from 'react';
-
-enum UtilityButtonState {
-    Default,
-    OpenRight,
-    OpenExternally,
-}
+import { Colors } from './global/globalEnums';
+import { DocumentView, OpenWhere } from './nodes/DocumentView';
 
 @observer
 export class PropertiesButtons extends React.Component<{}, {}> {
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 58d3b72e8..244cd4aa0 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -7,10 +7,10 @@ import { DocumentType } from '../documents/DocumentTypes';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SettingsManager } from '../util/SettingsManager';
+import './PropertiesDocBacklinksSelector.scss';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { LinkMenu } from './linking/LinkMenu';
-import { OpenWhere, OpenWhereMod } from './nodes/DocumentView';
-import './PropertiesDocBacklinksSelector.scss';
+import { OpenWhere } from './nodes/DocumentView';
 
 type PropertiesDocBacklinksSelectorProps = {
     Document: Doc;
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index 2b9bfb505..3c9fa1123 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,11 +1,9 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import './PropertiesSection.scss';
-import { Doc } from '../../fields/Doc';
-import { StrCast } from '../../fields/Types';
+import * as React from 'react';
 import { SettingsManager } from '../util/SettingsManager';
+import './PropertiesSection.scss';
 
 export interface PropertiesSectionProps {
     title: string;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 48653f30a..791928b4a 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,5 +1,6 @@
 import { action, computed, observable, ObservableSet, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast } from '../../fields/Doc';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, StrCast } from '../../fields/Types';
@@ -13,7 +14,6 @@ import { CollectionTreeView } from './collections/CollectionTreeView';
 import { DocumentView } from './nodes/DocumentView';
 import { DefaultStyleProvider } from './StyleProvider';
 import './TemplateMenu.scss';
-import * as React from 'react';
 
 @observer
 class TemplateToggle extends React.Component<{ template: string; checked: boolean; toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index f07e38af1..ea038250e 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -15,8 +15,8 @@ interface UndoStackProps {
 }
 @observer
 export class UndoStack extends React.Component<UndoStackProps> {
-    @observable static HideInline: boolean;
-    @observable static Expand: boolean;
+    @observable static HideInline: boolean = false;
+    @observable static Expand: boolean = false;
     render() {
         const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.userVariantColor;
         const color = UndoManager.batchCounter.get() ? 'black' : SettingsManager.userColor;
@@ -39,19 +39,25 @@ export class UndoStack extends React.Component<UndoStackProps> {
                                         color,
                                     }}>
                                     {Array.from(UndoManager.undoStackNames).map((name, i) => (
-                                        <div className="undoStack-resultContainer" key={i} 
-                                             onClick={e => {
+                                        <div
+                                            className="undoStack-resultContainer"
+                                            key={i}
+                                            onClick={e => {
                                                 const size = UndoManager.undoStackNames.length;
-                                                for (let n = 0; n < size-i; n++ ) UndoManager.Undo();  } } 
-                                            >
+                                                for (let n = 0; n < size - i; n++) UndoManager.Undo();
+                                            }}>
                                             <div className="undoStack-commandString">{StrCast(name).replace(/[^\.]*\./, '')}</div>
                                         </div>
                                     ))}
                                     {Array.from(UndoManager.redoStackNames)
                                         .reverse()
                                         .map((name, i) => (
-                                            <div className="undoStack-resultContainer" key={i} onClick={e =>
-                                                { for (let n = 0; n <= i; n++ ) UndoManager.Redo() }}>
+                                            <div
+                                                className="undoStack-resultContainer"
+                                                key={i}
+                                                onClick={e => {
+                                                    for (let n = 0; n <= i; n++) UndoManager.Redo();
+                                                }}>
                                                 <div className="undoStack-commandString" style={{ fontWeight: 'bold', background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}>
                                                     {StrCast(name).replace(/[^\.]*\./, '')}
                                                 </div>
diff --git a/src/client/views/animationtimeline/Region.tsx b/src/client/views/animationtimeline/Region.tsx
index 15cbbc16f..99163f6c6 100644
--- a/src/client/views/animationtimeline/Region.tsx
+++ b/src/client/views/animationtimeline/Region.tsx
@@ -6,11 +6,11 @@ import { List } from '../../../fields/List';
 import { createSchema, defaultSpec, listSpec, makeInterface } from '../../../fields/Schema';
 import { Cast, NumCast } from '../../../fields/Types';
 import { Transform } from '../../util/Transform';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import '../global/globalCssVariables.module.scss';
 import './Region.scss';
 import './Timeline.scss';
 import { TimelineMenu } from './TimelineMenu';
-import { ObservableReactComponent } from '../ObservableReactComponent';
 
 /**
  * Useful static functions that you can use. Mostly for logic, but you can also add UI logic here also
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index f27a2d2fd..cc4da1694 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -4,17 +4,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
 import clamp from '../../util/clamp';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { FieldViewProps } from '../nodes/FieldView';
 import { RegionHelpers } from './Region';
 import './Timeline.scss';
 import { TimelineOverview } from './TimelineOverview';
 import { Track } from './Track';
-import { ObservableReactComponent } from '../ObservableReactComponent';
 
 /**
  * Timeline class controls most of timeline functions besides individual region and track mechanism. Main functions are 
diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx
index 82ac69a3b..928739b53 100644
--- a/src/client/views/animationtimeline/TimelineOverview.tsx
+++ b/src/client/views/animationtimeline/TimelineOverview.tsx
@@ -28,7 +28,7 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps> {
     @observable private overviewBarWidth: number = 0;
     @observable private playbarWidth: number = 0;
     @observable private activeOverviewWidth: number = 0;
-    @observable private _authoringReaction?: IReactionDisposer;
+    @observable private _authoringReaction?: IReactionDisposer = undefined;
     @observable private visibleTime: number = 0;
     @observable private currentX: number = 0;
     @observable private visibleStart: number = 0;
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 00aa51cac..490a14be5 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -8,10 +8,10 @@ import { ObjectField } from '../../../fields/ObjectField';
 import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast } from '../../../fields/Types';
 import { Transform } from '../../util/Transform';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { Region, RegionData, RegionHelpers } from './Region';
 import { Timeline } from './Timeline';
 import './Track.scss';
-import { ObservableReactComponent } from '../ObservableReactComponent';
 
 interface IProps {
     timeline: Timeline;
@@ -29,9 +29,9 @@ interface IProps {
 @observer
 export class Track extends ObservableReactComponent<IProps> {
     @observable private _inner = React.createRef<HTMLDivElement>();
-    @observable private _currentBarXReaction: any;
-    @observable private _timelineVisibleReaction: any;
-    @observable private _autoKfReaction: any;
+    @observable private _currentBarXReaction: any = undefined;
+    @observable private _timelineVisibleReaction: any = undefined;
+    @observable private _autoKfReaction: any = undefined;
     @observable private _newKeyframe: boolean = false;
     private readonly MAX_TITLE_HEIGHT = 75;
     @observable private _trackHeight = 0;
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx
index 2cec29669..4c0a917f5 100644
--- a/src/client/views/collections/CollectionCalendarView.tsx
+++ b/src/client/views/collections/CollectionCalendarView.tsx
@@ -1,18 +1,11 @@
-import * as React from 'react';
-import { CollectionSubView } from './CollectionSubView';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
-import { computed, makeObservable, observable } from 'mobx';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import * as React from 'react';
+import { dateRangeStrToDates, emptyFunction, returnTrue } from '../../../Utils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
 import { CollectionStackingView } from './CollectionStackingView';
-import { CollectionViewType } from '../../documents/DocumentTypes';
-import { dateRangeStrToDates, emptyFunction, returnAll, returnEmptyDoclist, returnNone, returnOne, returnTrue } from '../../../Utils';
-import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
-import { TbRuler } from 'react-icons/tb';
-import { Transform } from '../../util/Transform';
-import { DocData } from '../../../fields/DocSymbols';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { StyleProp } from '../StyleProvider';
-import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { CollectionSubView } from './CollectionSubView';
 
 @observer
 export class CollectionCalendarView extends CollectionSubView() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 75c178136..868df7a3b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,4 +1,4 @@
-import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
@@ -30,7 +30,7 @@ import { ScriptingRepl } from '../ScriptingRepl';
 import { UndoStack } from '../UndoStack';
 import './CollectionDockingView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { CollectionSubView } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 443aa3f17..2a6cb081f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -32,16 +32,13 @@ interface CollectionMenuProps {
 @observer
 export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
     @observable static Instance: CollectionMenu;
-
     @observable SelectedCollection: DocumentView | undefined = undefined;
-    @observable FieldKey: string;
 
     private _docBtnRef = React.createRef<HTMLDivElement>();
 
     constructor(props: any) {
         super(props);
         makeObservable(this);
-        this.FieldKey = '';
         CollectionMenu.Instance = this;
         this._canFade = false; // don't let the inking menu fade away
         this.Pinned = Cast(Doc.UserDoc()['menuCollections-pinned'], 'boolean', true);
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index af822d917..2aad48d4a 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -1,4 +1,4 @@
-import { action, observable, trace } from 'mobx';
+import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 170b1f1ec..e5068645f 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,5 +1,6 @@
 import { action, computed, IReactionDisposer, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { ScriptField } from '../../../fields/ScriptField';
 import { NumCast, StrCast } from '../../../fields/Types';
@@ -12,7 +13,6 @@ import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import './CollectionPileView.scss';
 import { CollectionSubView } from './CollectionSubView';
-import * as React from 'react';
 
 @observer
 export class CollectionPileView extends CollectionSubView() {
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index fb8bc4da2..fa3844aa4 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -20,7 +20,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { CollectionSubView, SubCollectionViewProps } from '../collections/CollectionSubView';
+import { CollectionSubView } from '../collections/CollectionSubView';
 import { LightboxView } from '../LightboxView';
 import { AudioWaveform } from '../nodes/audio/AudioWaveform';
 import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView';
@@ -56,7 +56,7 @@ export enum TrimScope {
 @observer
 export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
     @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
-    @observable public static CurrentlyPlaying: DocumentView[];
+    @observable public static CurrentlyPlaying: DocumentView[] = [];
     constructor(props: any) {
         super(props);
         makeObservable(this);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index f9d575da2..69f1c332d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -20,8 +20,8 @@ import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import './CollectionStackingView.scss';
 import { ObservableReactComponent } from '../ObservableReactComponent';
+import './CollectionStackingView.scss';
 
 // So this is how we are storing a column
 interface CSVFieldColumnProps {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 2ff435ce2..570330174 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -4,11 +4,11 @@ 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, DocData } from '../../../fields/DocSymbols';
+import { AclPrivate } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+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';
@@ -41,8 +41,8 @@ export function CollectionSubView<X>(moreProps?: X) {
             makeObservable(this);
         }
 
-        @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
+        @observable _focusFilters: Opt<string[]> = undefined; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
+        @observable _focusRangeFilters: Opt<string[]> = undefined; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it
         protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
             this.dropDisposer?.();
             this.gestureDisposer?.();
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 6033b897d..7bbdb7073 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,6 +1,8 @@
 import { toUpper } from 'lodash';
 import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
 import { Doc, Opt, StrListCast } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
@@ -8,7 +10,6 @@ import { RichTextField } from '../../../fields/RichTextField';
 import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -17,20 +18,19 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
 import { PresBox } from '../nodes/trails';
-import { computePivotLayout, computeTimelineLayout, ViewDefBounds } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionSubView } from './CollectionSubView';
 import './CollectionTimeView.scss';
-import * as React from 'react';
+import { ViewDefBounds, computePivotLayout, computeTimelineLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 
 @observer
 export class CollectionTimeView extends CollectionSubView() {
     _changing = false;
     @observable _layoutEngine = computePivotLayout.name;
     @observable _collapsed: boolean = false;
-    @observable _childClickedScript: Opt<ScriptField>;
-    @observable _viewDefDivClick: Opt<ScriptField>;
-    @observable _focusPivotField: Opt<string>;
+    @observable _childClickedScript: Opt<ScriptField> = undefined;
+    @observable _viewDefDivClick: Opt<ScriptField> = undefined;
+    @observable _focusPivotField: Opt<string> = undefined;
 
     constructor(props: any) {
         super(props);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0656843cb..c2f5ab2c0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -16,6 +16,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { OpenWhere } from '../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import { CollectionCalendarView } from './CollectionCalendarView';
 import { CollectionCarousel3DView } from './CollectionCarousel3DView';
 import { CollectionCarouselView } from './CollectionCarouselView';
 import { CollectionDockingView } from './CollectionDockingView';
@@ -28,7 +29,6 @@ import { CollectionTreeView } from './CollectionTreeView';
 import './CollectionView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
-import { CollectionCalendarView } from './CollectionCalendarView';
 import { CollectionLinearView } from './collectionLinear';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
index f3071b316..4523a4f1e 100644
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -1,6 +1,6 @@
-import * as React from "react";
-import { observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
 
 interface IKeyRestrictionProps {
     contains: boolean;
@@ -19,37 +19,28 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr
         if (this._key && this._value) {
             let parsedValue: string | number = `"${this._value}"`;
             const parsed = parseInt(this._value);
-            let type = "string";
+            let type = 'string';
             if (!isNaN(parsed)) {
                 parsedValue = parsed;
-                type = "number";
+                type = 'number';
             }
-            const scriptText = `${this._contains ? "" : "!"}(((doc.${this._key} && (doc.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))) ||
-                ((doc.data_ext && doc.data_ext.${this._key}) && (doc.data_ext.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))))`;
+            const scriptText = `${this._contains ? '' : '!'}(((doc.${this._key} && (doc.${this._key} as ${type})${type === 'string' ? '.includes' : '<='}(${parsedValue}))) ||
+                ((doc.data_ext && doc.data_ext.${this._key}) && (doc.data_ext.${this._key} as ${type})${type === 'string' ? '.includes' : '<='}(${parsedValue}))))`;
             // let doc = new Doc();
             // ((doc.data_ext && doc.data_ext!.text) && (doc.data_ext!.text as string).includes("hello"));
             this.props.script(scriptText);
-        }
-        else {
-            this.props.script("");
+        } else {
+            this.props.script('');
         }
 
         return (
             <div className="collectionViewBaseChrome-viewSpecsMenu-row">
-                <input className="collectionViewBaseChrome-viewSpecsMenu-rowLeft"
-                    value={this._key}
-                    onChange={(e) => runInAction(() => this._key = e.target.value)}
-                    placeholder="KEY" />
-                <button className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
-                    style={{ background: this._contains ? "#77dd77" : "#ff6961" }}
-                    onClick={() => runInAction(() => this._contains = !this._contains)}>
-                    {this._contains ? "CONTAINS" : "DOES NOT CONTAIN"}
+                <input className="collectionViewBaseChrome-viewSpecsMenu-rowLeft" value={this._key} onChange={e => runInAction(() => (this._key = e.target.value))} placeholder="KEY" />
+                <button className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle" style={{ background: this._contains ? '#77dd77' : '#ff6961' }} onClick={() => runInAction(() => (this._contains = !this._contains))}>
+                    {this._contains ? 'CONTAINS' : 'DOES NOT CONTAIN'}
                 </button>
-                <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
-                    value={this._value}
-                    onChange={(e) => runInAction(() => this._value = e.target.value)}
-                    placeholder="VALUE" />
+                <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight" value={this._value} onChange={e => runInAction(() => (this._value = e.target.value))} placeholder="VALUE" />
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index a7705ea7e..72882ac17 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -93,7 +93,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
     static _openLevelScript: Opt<ScriptField | undefined>;
     private _header: React.RefObject<HTMLDivElement> = React.createRef();
     private _tref = React.createRef<HTMLDivElement>();
-    @observable _docRef: Opt<DocumentView>;
+    @observable _docRef: Opt<DocumentView> = undefined;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _editTitleScript: (() => ScriptField) | undefined;
     private _openScript: (() => ScriptField) | undefined;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
index 99ee5ef4e..08dfb32ad 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
@@ -1,8 +1,8 @@
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast } from '../../../../fields/Types';
 import './CollectionFreeFormView.scss';
-import * as React from 'react';
 
 export interface CollectionFreeFormViewBackgroundGridProps {
     panX: () => number;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
index 2606304d0..58f6b1593 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -1,5 +1,5 @@
 import { IconButton, Size, Type } from 'browndash-components';
-import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { IReactionDisposer, action, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { SettingsManager } from '../../../util/SettingsManager';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 15e4d8360..8628ca3c3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -1,16 +1,16 @@
-import { IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
+import { makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Field, FieldResult } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
+import { StrCast } from '../../../../fields/Types';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { LinkManager } from '../../../util/LinkManager';
-import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
-import { CollectionFreeFormInfoState, InfoState, infoState, StateEntryFunc } from './CollectionFreeFormInfoState';
+import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton';
+import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
 import { CollectionFreeFormView } from './CollectionFreeFormView';
 import './CollectionFreeFormView.scss';
-import { StrCast } from '../../../../fields/Types';
 
 export interface CollectionFreeFormInfoUIProps {
     Document: Doc;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 779a0bf96..95d521f65 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,11 +1,11 @@
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Id } from '../../../../fields/FieldSymbols';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { LightboxView } from '../../LightboxView';
-import './CollectionFreeFormLinksView.scss';
 import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
-import * as React from 'react';
+import './CollectionFreeFormLinksView.scss';
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index f54726a00..ec8416303 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -1,12 +1,11 @@
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from '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 './CollectionFreeFormView.scss';
 export interface CollectionFreeFormPannableContentsProps {
     Document: Doc;
     viewDefDivClick?: ScriptField;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 45e24bbb2..fa8218bdd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,6 +1,8 @@
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as mobxUtils from 'mobx-utils';
+import * as React from 'react';
+import * as uuid from 'uuid';
 import CursorField from '../../../../fields/CursorField';
 import { Doc, FieldResult } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
@@ -9,8 +11,6 @@ import { listSpec } from '../../../../fields/Schema';
 import { Cast } from '../../../../fields/Types';
 import { CollectionViewProps } from '../CollectionView';
 import './CollectionFreeFormView.scss';
-import * as React from 'react';
-import * as uuid from 'uuid';
 
 @observer
 export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 645e9cff7..cc0833698 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -37,6 +37,7 @@ import { CtrlKey } from '../../GlobalKeyHandler';
 import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
 import { LightboxView } from '../../LightboxView';
 import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewWrapper } from '../../nodes/CollectionFreeFormDocumentView';
+import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
 import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView';
 import { FieldViewProps } from '../../nodes/FieldView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
@@ -52,7 +53,6 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
 import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
 import './CollectionFreeFormView.scss';
 import { MarqueeView } from './MarqueeView';
-import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
 
 export type collectionFreeformViewProps = {
     NativeWidth?: () => number;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b22fdfa19..39d828302 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -16,19 +16,18 @@ import { DocumentType } from '../../../documents/DocumentTypes';
 import { DocUtils, Docs, DocumentOptions } from '../../../documents/Documents';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { freeformScrollMode } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 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 { OpenWhere } from '../../nodes/DocumentView';
 import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { SubCollectionViewProps } from '../CollectionSubView';
 import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
 import './MarqueeView.scss';
-import { SnappingManager } from '../../../util/SnappingManager';
-
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
     getTransform: () => Transform;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 1e19964d7..3e75257e5 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -7,7 +7,6 @@ import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types
 import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
@@ -17,13 +16,12 @@ import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionGridView.scss';
 import Grid, { Layout } from './Grid';
-
 @observer
 export class CollectionGridView extends CollectionSubView() {
     private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
     private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
-    @observable private _rowHeight: Opt<number>; // temporary store of row height to make change undoable
+    @observable private _rowHeight: Opt<number> = undefined; // temporary store of row height to make change undoable
     @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
 
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index 3d1d87aa0..9145d7ef1 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -1,5 +1,5 @@
-import * as React from 'react';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 
 import '../../../../../node_modules/react-grid-layout/css/styles.css';
 import '../../../../../node_modules/react-resizable/css/styles.css';
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index e12bcd8b0..5bea59e7b 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -4,13 +4,13 @@ import { Button } from 'browndash-components';
 import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { returnFalse } from '../../../../Utils';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoBatch, undoable } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionMulticolumnView.scss';
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 7dbc18e60..3043eb0f8 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,9 +1,9 @@
 import { action, computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { returnFalse } from '../../../../Utils';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -12,7 +12,6 @@ import { CollectionSubView } from '../CollectionSubView';
 import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
-
 interface HeightSpecifier {
     magnitude: number;
     unit: string;
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 868b1140d..ea99bff2e 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,12 +1,12 @@
-import * as React from 'react';
+import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
+import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { DimUnit } from './CollectionMulticolumnView';
 import { UndoManager } from '../../../util/UndoManager';
-import { StyleProviderFunc } from '../../nodes/DocumentView';
 import { StyleProp } from '../../StyleProvider';
+import { StyleProviderFunc } from '../../nodes/DocumentView';
+import { DimUnit } from './CollectionMulticolumnView';
 
 interface ResizerProps {
     width: number;
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
index 9985a9fba..a9579d931 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
@@ -1,27 +1,25 @@
-import * as React from "react";
-import { observer } from "mobx-react";
-import { computed } from "mobx";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
-import { EditableView } from "../../EditableView";
-import { DimUnit } from "./CollectionMulticolumnView";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { EditableView } from '../../EditableView';
+import { DimUnit } from './CollectionMulticolumnView';
 
 interface WidthLabelProps {
     layout: Doc;
     collectionDoc: Doc;
-    decimals?: number;
 }
 
 @observer
 export default class WidthLabel extends React.Component<WidthLabelProps> {
-
     @computed
     private get contents() {
-        const { layout, decimals } = this.props;
+        const { layout } = this.props;
         const getUnit = () => StrCast(layout.dimUnit);
-        const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(decimals ?? 3));
+        const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(3));
         return (
-            <div className={"label-wrapper"}>
+            <div className={'label-wrapper'}>
                 <EditableView
                     GetValue={getMagnitude}
                     SetValue={value => {
@@ -50,7 +48,6 @@ export default class WidthLabel extends React.Component<WidthLabelProps> {
     }
 
     render() {
-        return BoolCast(this.props.collectionDoc.showWidthLabels) ? this.contents : (null);
+        return BoolCast(this.props.collectionDoc.showWidthLabels) ? this.contents : null;
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
index aa5439fa4..878c7ff3c 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -1,10 +1,10 @@
-import * as React from "react";
-import { observer } from "mobx-react";
-import { computed } from "mobx";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
-import { EditableView } from "../../EditableView";
-import { DimUnit } from "./CollectionMultirowView";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { EditableView } from '../../EditableView';
+import { DimUnit } from './CollectionMultirowView';
 
 interface HeightLabelProps {
     layout: Doc;
@@ -14,14 +14,13 @@ interface HeightLabelProps {
 
 @observer
 export default class HeightLabel extends React.Component<HeightLabelProps> {
-
     @computed
     private get contents() {
         const { layout, decimals } = this.props;
         const getUnit = () => StrCast(layout.dimUnit);
         const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(decimals ?? 3));
         return (
-            <div className={"label-wrapper"}>
+            <div className={'label-wrapper'}>
                 <EditableView
                     GetValue={getMagnitude}
                     SetValue={value => {
@@ -50,7 +49,6 @@ export default class HeightLabel extends React.Component<HeightLabelProps> {
     }
 
     render() {
-        return BoolCast(this.props.collectionDoc.showHeightLabels) ? this.contents : (null);
+        return BoolCast(this.props.collectionDoc.showHeightLabels) ? this.contents : null;
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 5a9d6a82c..7dee65e58 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,12 +1,12 @@
-import * as React from 'react';
+import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
+import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { DimUnit } from './CollectionMultirowView';
 import { UndoManager } from '../../../util/UndoManager';
 import { StyleProp } from '../../StyleProvider';
 import { StyleProviderFunc } from '../../nodes/DocumentView';
+import { DimUnit } from './CollectionMultirowView';
 
 interface ResizerProps {
     height: number;
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 04443b4a7..5f8b412be 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,7 +1,7 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
+import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { Colors } from '../../global/globalEnums';
 import './CollectionSchemaView.scss';
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 06073b52c..85e97f95f 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,3 +1,4 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { action, computed, makeObservable, observable } from 'mobx';
@@ -7,18 +8,17 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { Cast, DocCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
+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 { LinkInfo } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
-import { undoBatch } from '../../util/UndoManager';
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { DocumentType } from '../../documents/DocumentTypes';
 
 interface LinkMenuItemProps {
     groupType: string;
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 5460a6daf..7e344dd7a 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -1,16 +1,16 @@
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { EditorView } from 'prosemirror-view';
-import { Doc } from '../../../fields/Doc';
+import * as React from 'react';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
+import { Doc } from '../../../fields/Doc';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
+import { DefaultStyleProvider } from '../StyleProvider';
 import { OpenWhere } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { SearchBox } from '../search/SearchBox';
-import { DefaultStyleProvider } from '../StyleProvider';
 import './LinkPopup.scss';
-import * as React from 'react';
 
 interface LinkPopupProps {
     linkFrom?: () => Doc | undefined;
@@ -29,7 +29,7 @@ interface LinkPopupProps {
 @observer
 export class LinkPopup extends React.Component<LinkPopupProps> {
     @observable private linkURL: string = '';
-    @observable public view?: EditorView;
+    @observable public view?: EditorView = undefined;
 
     // TODO: should check for valid URL
     @undoBatch
diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx
index 3e7f5be74..0902d53b2 100644
--- a/src/client/views/linking/LinkRelationshipSearch.tsx
+++ b/src/client/views/linking/LinkRelationshipSearch.tsx
@@ -1,6 +1,6 @@
 import { observer } from 'mobx-react';
-import './LinkEditor.scss';
 import * as React from 'react';
+import './LinkEditor.scss';
 
 interface link_relationshipSearchProps {
     results: string[] | undefined;
diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
index 72b63cf8f..bce2b296f 100644
--- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
+++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
@@ -3,12 +3,12 @@ import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { SelectionManager } from '../../../util/SelectionManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
-import { DocumentView, OpenWhereMod } from '../../nodes/DocumentView';
+import { OpenWhereMod } from '../../nodes/DocumentView';
 import { NewLightboxView } from '../NewLightboxView';
 import './ButtonMenu.scss';
 import { IButtonMenu } from './utils';
-import { SnappingManager } from '../../../util/SnappingManager';
 
 export const ButtonMenu = (props: IButtonMenu) => {
     return (
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index 6980e31c1..3d159c3e3 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -2,28 +2,28 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { LinkManager } from '../../util/LinkManager';
 import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline';
-import { TabDocView } from '../collections/TabDocView';
 import { GestureOverlay } from '../GestureOverlay';
 import { LightboxView } from '../LightboxView';
-import { DocumentView, OpenWhere } from '../nodes/DocumentView';
 import { DefaultStyleProvider } from '../StyleProvider';
-import { IRecommendation } from './components';
+import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline';
+import { TabDocView } from '../collections/TabDocView';
+import { DocumentView, OpenWhere } from '../nodes/DocumentView';
 import { ExploreView } from './ExploreView';
-import { emptyBounds, IBounds } from './ExploreView/utils';
+import { IBounds, emptyBounds } from './ExploreView/utils';
 import { NewLightboxHeader } from './Header';
 import './NewLightboxView.scss';
 import { RecommendationList } from './RecommendationList';
-import { SnappingManager } from '../../util/SnappingManager';
+import { IRecommendation } from './components';
 
 enum LightboxStatus {
     RECOMMENDATIONS = 'recommendations',
@@ -50,15 +50,15 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
         return this._doc;
     }
     private static LightboxDocTemplate = () => NewLightboxView._layoutTemplate;
-    @observable private static _layoutTemplate: Opt<Doc>;
-    @observable private static _layoutTemplateString: Opt<string>;
-    @observable private static _doc: Opt<Doc>;
-    @observable private static _docTarget: Opt<Doc>;
+    @observable private static _layoutTemplate: Opt<Doc> = undefined;
+    @observable private static _layoutTemplateString: Opt<string> = undefined;
+    @observable private static _doc: Opt<Doc> = undefined;
+    @observable private static _docTarget: Opt<Doc> = undefined;
     @observable private static _docFilters: string[] = []; // filters
-    private static _savedState: Opt<LightboxSavedState>;
+    private static _savedState: Opt<LightboxSavedState> = undefined;
     private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
     @observable private static _future: Opt<Doc[]> = [];
-    @observable private static _docView: Opt<DocumentView>;
+    @observable private static _docView: Opt<DocumentView> = undefined;
 
     // keywords
     @observable private static _keywords: string[] = [];
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 4c36d2fcb..12e8e1a69 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -44,8 +44,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
     crop: ((region: Doc | undefined, addCrop?: boolean) => Doc | undefined) | undefined;
-    @observable schemaDataVizChildren: any;
-    @observable _marqueeing: number[] | undefined;
+    @observable schemaDataVizChildren: any = undefined;
+    @observable _marqueeing: number[] | undefined = undefined;
     @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get annotationLayer() {
         TraceMobx();
diff --git a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
index 3cb5125da..24023077f 100644
--- a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
+++ b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
@@ -1,15 +1,14 @@
-import * as React from 'react';
-import './SchemaCSVPopUp.scss';
+import { IconButton } from 'browndash-components';
 import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { CgClose } from 'react-icons/cg';
+import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { Doc } from '../../../../fields/Doc';
-import { Button, IconButton, Type } from 'browndash-components';
 import { StrCast } from '../../../../fields/Types';
-import { MarqueeView } from '../../collections/collectionFreeForm/MarqueeView';
-import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
 import { DocumentView } from '../DocumentView';
-import { CgClose } from 'react-icons/cg';
+import './SchemaCSVPopUp.scss';
 
 interface SchemaCSVPopUpProps {}
 
@@ -56,13 +55,11 @@ export class SchemaCSVPopUp extends React.Component<SchemaCSVPopUpProps> {
             <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                 {this.heading('Schema Table as Data Visualization Doc')}
                 <div className="image-content-wrapper">
-                        <div className="img-wrapper">
-                            <div className="img-container" onPointerDown={e => this.drag(e)}>
-                                <img
-                                    width={150} height={150} src={"/assets/dataVizBox.png"}
-                                />
-                            </div>
+                    <div className="img-wrapper">
+                        <div className="img-container" onPointerDown={e => this.drag(e)}>
+                            <img width={150} height={150} src={'/assets/dataVizBox.png'} />
                         </div>
+                    </div>
                 </div>
             </div>
         );
@@ -88,9 +85,10 @@ export class SchemaCSVPopUp extends React.Component<SchemaCSVPopUpProps> {
                     return embedding;
                 };
                 if (this.view && sourceAnchorCreator && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
-                    DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], 
-                        new DragManager.AnchorAnnoDragData(this.view, sourceAnchorCreator, targetCreator), downX, downY, {
-                        dragComplete: e => {this.setVisible(false);},
+                    DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.view, sourceAnchorCreator, targetCreator), downX, downY, {
+                        dragComplete: e => {
+                            this.setVisible(false);
+                        },
                     });
                     return true;
                 }
@@ -108,4 +106,4 @@ export class SchemaCSVPopUp extends React.Component<SchemaCSVPopUpProps> {
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index 227c993c7..9e9a43b34 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
 import * as d3 from 'd3';
-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 { FaFillDrip } from 'react-icons/fa';
@@ -11,10 +11,10 @@ import { listSpec } from '../../../../../fields/Schema';
 import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
 import { Docs } from '../../../../documents/Documents';
 import { undoable } from '../../../../util/UndoManager';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
 import { PinProps, PresBox } from '../../trails';
 import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
 import './Chart.scss';
-import { ObservableReactComponent } from '../../../ObservableReactComponent';
 
 export interface HistogramProps {
     Document: Doc;
@@ -461,7 +461,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
 
         if (this._histogramData.length > 0 || !this.parentViz) {
             return this._props.axes.length >= 1 ? (
-                <div className="chart-container" style={{width: this._props.width+this._props.margin.right}}>
+                <div className="chart-container" style={{ width: this._props.width + this._props.margin.right }}>
                     <div className="graph-title">
                         <EditableText
                             val={StrCast(this._props.layoutDoc[titleAccessor])}
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 4b0df0457..a69f083d1 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -1,6 +1,6 @@
 import { Button, EditableText, Size } from 'browndash-components';
 import * as d3 from 'd3';
-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 { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc';
@@ -10,11 +10,11 @@ import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
 import { Docs } from '../../../../documents/Documents';
 import { DocumentManager } from '../../../../util/DocumentManager';
 import { undoable } from '../../../../util/UndoManager';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
 import { PinProps, PresBox } from '../../trails';
 import { DataVizBox } from '../DataVizBox';
 import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils';
 import './Chart.scss';
-import { ObservableReactComponent } from '../../../ObservableReactComponent';
 
 export interface DataPoint {
     x: number;
@@ -48,12 +48,11 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
     private _lineChartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
     @observable _currSelected: SelectedDataPoint | undefined = undefined;
     // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
-    constructor(props:any) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
     }
 
-
     @computed get _tableDataIds() {
         return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
     }
@@ -359,7 +358,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
         const selectedPt = this._currSelected ? `{ ${this._props.axes[0]}: ${this._currSelected.x} ${this._props.axes[1]}: ${this._currSelected.y} }` : 'none';
         if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length == 0) {
             return this._props.axes.length >= 2 && /\d/.test(this._props.records[0][this._props.axes[0]]) && /\d/.test(this._props.records[0][this._props.axes[1]]) ? (
-                <div className="chart-container" style={{width: this.props.width+this.props.margin.right}}>
+                <div className="chart-container" style={{ width: this.props.width + this.props.margin.right }}>
                     <div className="graph-title">
                         <EditableText
                             val={StrCast(this._props.layoutDoc[titleAccessor])}
@@ -373,16 +372,17 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
                         />
                     </div>
                     <div ref={this._lineChartRef} />
-                    {selectedPt != 'none' ? 
-                        <div className={'selected-data'}> 
+                    {selectedPt != 'none' ? (
+                        <div className={'selected-data'}>
                             {`Selected: ${selectedPt}`}
-                            <Button onClick={e=>{
-                                console.log("test plzz")
-                                this.props.vizBox.sidebarBtnDown;
-                                this.props.vizBox.sidebarAddDocument;}
-                                }></Button>
-                        </div> 
-                        : null}
+                            <Button
+                                onClick={e => {
+                                    console.log('test plzz');
+                                    this.props.vizBox.sidebarBtnDown;
+                                    this.props.vizBox.sidebarAddDocument;
+                                }}></Button>
+                        </div>
+                    ) : null}
                 </div>
             ) : (
                 <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</span>
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index e644870da..e67556cd0 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -1,7 +1,7 @@
 import { Checkbox } from '@mui/material';
 import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
 import * as d3 from 'd3';
-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 { FaFillDrip } from 'react-icons/fa';
@@ -11,9 +11,9 @@ import { listSpec } from '../../../../../fields/Schema';
 import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
 import { Docs } from '../../../../documents/Documents';
 import { undoable } from '../../../../util/UndoManager';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
 import { PinProps, PresBox } from '../../trails';
 import './Chart.scss';
-import { ObservableReactComponent } from '../../../ObservableReactComponent';
 
 export interface PieChartProps {
     Document: Doc;
@@ -356,7 +356,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
 
         if (this._pieChartData.length > 0 || !this.parentViz) {
             return this._props.axes.length >= 1 ? (
-                <div className="chart-container" style={{width: this._props.width+this._props.margin.right}}>
+                <div className="chart-container" style={{ width: this._props.width + this._props.margin.right }}>
                     <div className="graph-title">
                         <EditableText
                             val={StrCast(this._props.layoutDoc[titleAccessor])}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 4f60a156b..f127fecf3 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,17 +1,17 @@
 import { Button, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction, trace } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../../Utils';
 import { Doc, Field, NumListCast } from '../../../../../fields/Doc';
 import { List } from '../../../../../fields/List';
 import { listSpec } from '../../../../../fields/Schema';
 import { Cast, DocCast } from '../../../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils';
 import { DragManager } from '../../../../util/DragManager';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
 import { DocumentView } from '../../DocumentView';
 import { DataVizView } from '../DataVizBox';
 import './Chart.scss';
-import { ObservableReactComponent } from '../../../ObservableReactComponent';
 const {  default: { DATA_VIZ_TABLE_ROW_HEIGHT } } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore
 interface TableBoxProps {
     Document: Doc;
@@ -87,7 +87,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
         return (this.viewScale * this._tableHeight) / this._tableDataIds.length;
     }
     @computed get startID() {
-        return this.rowHeight ? Math.max(Math.floor(this._scrollTop / this.rowHeight)-1, 0) : 0;
+        return this.rowHeight ? Math.max(Math.floor(this._scrollTop / this.rowHeight) - 1, 0) : 0;
     }
     @computed get endID() {
         console.log('start = ' + this.startID + '  container = ' + this._tableContainerHeight + ' scale = ' + this.viewScale + ' row = ' + this.rowHeight);
@@ -169,7 +169,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
             return (
                 <div
                     className="tableBox"
-                    style={{width: this.props.width+this.props.margin.right}}
+                    style={{ width: this.props.width + this.props.margin.right }}
                     tabIndex={0}
                     onKeyDown={e => {
                         if (this._props.layoutDoc && e.key === 'a' && (e.ctrlKey || e.metaKey)) {
@@ -226,7 +226,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
                             </thead>
                             <tbody>
                                 {this._tableDataIds
-                                    .filter((rowId, i) => this.startID-2 <= i && i <= this.endID+2)
+                                    .filter((rowId, i) => this.startID - 2 <= i && i <= this.endID + 2)
                                     ?.map(rowId => (
                                         <tr
                                             key={rowId}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index fa472312e..4cc7fe4c8 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,6 +1,7 @@
-import { computed, makeObservable, observable } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import JsxParser from 'react-jsx-parser';
 import * as XRegExp from 'xregexp';
 import { OmitKeys, Without, emptyPath } from '../../../Utils';
 import { Doc, Opt } from '../../../fields/Doc';
@@ -10,11 +11,11 @@ import { Cast, StrCast } from '../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { InkingStroke } from '../InkingStroke';
 import { ObservableReactComponent } from '../ObservableReactComponent';
+import { CollectionCalendarView } from '../collections/CollectionCalendarView';
 import { CollectionDockingView } from '../collections/CollectionDockingView';
 import { CollectionView } from '../collections/CollectionView';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { CollectionSchemaView } from '../collections/collectionSchema/CollectionSchemaView';
-import { CollectionCalendarView } from '../collections/CollectionCalendarView';
 import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
 import { PresElementBox } from '../nodes/trails/PresElementBox';
 import { SearchBox } from '../search/SearchBox';
@@ -47,7 +48,6 @@ import { WebBox } from './WebBox';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { ImportElementBox } from './importBox/ImportElementBox';
 import { PresBox } from './trails/PresBox';
-import JsxParser from 'react-jsx-parser';
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
 export interface JsxBindings {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cc7fca8d2..f8f4b94a2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,10 +1,11 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { Dropdown, DropdownType, Type } from 'browndash-components';
+import { Howl } from 'howler';
 import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
-import { Bounce, Fade, Flip, JackInTheBox, Roll, Rotate, Zoom } from 'react-awesome-reveal';
 import * as React from 'react';
+import { Bounce, Fade, Flip, JackInTheBox, Roll, Rotate, Zoom } from 'react-awesome-reveal';
 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';
@@ -53,7 +54,6 @@ import { LinkAnchorBox } from './LinkAnchorBox';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { PresEffect, PresEffectDirection } from './trails';
 import { PinProps, PresBox } from './trails/PresBox';
-import { Howl } from 'howler';
 
 interface Window {
     MediaRecorder: MediaRecorder;
@@ -138,7 +138,6 @@ export interface DocComponentView {
     dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
     incrementalRendering?: () => void;
     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 };
     ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
@@ -1516,7 +1515,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
             const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
             if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
         }
-        return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) };
+        return { left, top, right, bottom };
     };
 
     public iconify(finished?: () => void, animateTime?: number) {
diff --git a/src/client/views/nodes/FaceRectangle.tsx b/src/client/views/nodes/FaceRectangle.tsx
index 8d03bf57a..46bc6eb03 100644
--- a/src/client/views/nodes/FaceRectangle.tsx
+++ b/src/client/views/nodes/FaceRectangle.tsx
@@ -1,6 +1,6 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
 import { observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import { RectangleTemplate } from './FaceRectangles';
 
 @observer
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 26e720c0d..ade4225d9 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -1,8 +1,8 @@
+import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
-import { Cast, NumCast } from '../../../fields/Types';
-import { observer } from 'mobx-react';
 import { Id } from '../../../fields/FieldSymbols';
+import { Cast, NumCast } from '../../../fields/Types';
 import FaceRectangle from './FaceRectangle';
 
 interface FaceRectanglesProps {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index f4c5167a5..008f10f26 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,6 +1,6 @@
-import * as React from 'react';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { DateField } from '../../../fields/DateField';
 import { Doc, Field, FieldResult, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index fd8d8ef56..7bc9d3f85 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,6 @@
 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';
@@ -11,13 +10,13 @@ import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { EditableView } from '../EditableView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DefaultStyleProvider } from '../StyleProvider';
 import { OpenWhere } from './DocumentView';
 import { FieldViewProps } from './FieldView';
 import { KeyValueBox } from './KeyValueBox';
 import './KeyValueBox.scss';
 import './KeyValuePair.scss';
-import { ObservableReactComponent } from '../ObservableReactComponent';
 
 // Represents one row in a key value plane
 
@@ -35,12 +34,11 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
     @observable private isPointerOver = false;
     @observable public isChecked = false;
     private checkbox = React.createRef<HTMLInputElement>();
-    constructor(props:any) {
+    constructor(props: any) {
         super(props);
         makeObservable(this);
     }
 
-
     @action
     handleCheck = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.isChecked = e.currentTarget.checked;
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index 32300d60a..8ad0b7dde 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -1,6 +1,6 @@
-import * as React from 'react';
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc } from '../../../fields/Doc';
 import { LinkManager } from '../../util/LinkManager';
 import './LinkDescriptionPopup.scss';
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index d0a9f10b4..ea23ecbea 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -2,13 +2,14 @@ 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 wiki from 'wikijs';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 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 { DocServer } from '../../DocServer';
-import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
@@ -16,10 +17,9 @@ import { LinkManager } from '../../util/LinkManager';
 import { SearchUtil } from '../../util/SearchUtil';
 import { SettingsManager } from '../../util/SettingsManager';
 import { Transform } from '../../util/Transform';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 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>;
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 27d73a585..adccc9db6 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -1,4 +1,4 @@
-import { action, observable, runInAction } from 'mobx';
+import { observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import ReactLoading from 'react-loading';
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
index 42dfa59b7..35153f439 100644
--- a/src/client/views/nodes/MapBox/AnimationUtility.ts
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -1,13 +1,10 @@
-import mapboxgl from 'mapbox-gl';
-import { MercatorCoordinate } from 'mapbox-gl';
-import { MapRef } from 'react-map-gl';
-import * as React from 'react';
-import * as d3 from 'd3';
 import * as turf from '@turf/turf';
 import { Position } from '@turf/turf';
-import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
-import { observer } from 'mobx-react';
-import { action, computed, observable, runInAction, makeObservable } from 'mobx';
+import * as d3 from 'd3';
+import { Feature, GeoJsonProperties, Geometry } from 'geojson';
+import mapboxgl, { MercatorCoordinate } from 'mapbox-gl';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
+import { MapRef } from 'react-map-gl';
 
 export enum AnimationStatus {
     START = 'start',
diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
index f9607becf..7e99795b5 100644
--- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
@@ -1,15 +1,15 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
-import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { IconLookup, faAdd, faCalendarDays, faRoute } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { IconButton } from 'browndash-components';
 import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
-import { Doc, Opt } from '../../../../fields/Doc';
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import { returnFalse, unimplementedFunction } from '../../../../Utils';
+import { Doc, Opt } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { SelectionManager } from '../../../util/SelectionManager';
-import { IconButton } from 'browndash-components';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { SettingsManager } from '../../../util/SettingsManager';
-import { IconLookup, faAdd, faCalendarDays, faRoute } from '@fortawesome/free-solid-svg-icons';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 
 @observer
 export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index 1b1b74e7c..08bea5d9d 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,26 +1,23 @@
+import { IconLookup, faAdd, faArrowDown, faArrowLeft, faArrowsRotate, faBicycle, faCalendarDays, faCar, faDiamondTurnRight, faEdit, faPersonWalking, faRoute } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import * as React from 'react';
+import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
+import { IconButton } from 'browndash-components';
+import { Position } from 'geojson';
 import { IReactionDisposer, ObservableMap, action, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
+import * as React from 'react';
+import { CirclePicker, ColorResult } from 'react-color';
 import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { CalendarManager } from '../../../util/CalendarManager';
 import { SelectionManager } from '../../../util/SelectionManager';
-import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
-// import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
-import { Button, IconButton } from 'browndash-components';
 import { SettingsManager } from '../../../util/SettingsManager';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 import './MapAnchorMenu.scss';
-import { NumCast, StrCast } from '../../../../fields/Types';
-import { IconLookup, faDiamondTurnRight, faCalendarDays, faEdit, faAdd, faRoute, faArrowLeft, faLocationDot, faArrowDown, faCar, faBicycle, faPersonWalking, faUpload, faArrowsRotate } from '@fortawesome/free-solid-svg-icons';
-import { DirectionsAnchorMenu } from './DirectionsAnchorMenu';
-import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
 import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
-import { MapBox } from './MapBox';
-import { List } from '../../../../fields/List';
 import { MarkerIcons } from './MarkerIcons';
-import { CirclePicker, ColorResult } from 'react-color';
-import { Position } from 'geojson';
-import { CalendarManager } from '../../../util/CalendarManager';
+// import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
 
 type MapAnchorMenuType = 'standard' | 'routeCreation' | 'calendar' | 'customize' | 'route';
 
@@ -52,7 +49,6 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     public UpdateMarkerColor: (color: string) => void = unimplementedFunction;
     public UpdateMarkerIcon: (iconKey: string) => void = unimplementedFunction;
 
-
     public Hide: () => void = unimplementedFunction;
 
     public OpenAnimationPanel: (routeDoc: Doc | undefined) => void = unimplementedFunction;
@@ -74,22 +70,21 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     private title: string | undefined = undefined;
 
     public setPinDoc(pinDoc: Doc | undefined) {
-        if (pinDoc){
+        if (pinDoc) {
             this.pinDoc = pinDoc;
             this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`);
         }
-        
     }
 
     public setRouteDoc(routeDoc: Doc | undefined) {
-        if (routeDoc){
+        if (routeDoc) {
             this.routeDoc = routeDoc;
             this.title = StrCast(routeDoc.title ?? 'Map route');
         }
     }
 
     @action
-    public Reset(){
+    public Reset() {
         this.destinationSelected = false;
         this.currentRouteInfoMap = undefined;
         this.destinationFeatures = [];
@@ -296,34 +291,23 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         return undefined;
     };
 
-    getDirectionsButton: JSX.Element = (
-        <IconButton 
-            tooltip="Get directions" 
-            onPointerDown={this.DirectionsClick}
-            icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup} />} 
-            color={SettingsManager.userColor} />
-    )
+    getDirectionsButton: JSX.Element = (<IconButton tooltip="Get directions" onPointerDown={this.DirectionsClick} icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup} />} color={SettingsManager.userColor} />);
 
     getAddToCalendarButton = (docType: string): JSX.Element => {
         return (
-            <IconButton 
-            tooltip="Add to calendar" 
-            onPointerDown={() => {
-                CalendarManager.Instance.open(undefined, docType === 'pin' ? this.pinDoc : this.routeDoc)
-            }} 
-            icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} 
-            color={SettingsManager.userColor} 
+            <IconButton
+                tooltip="Add to calendar"
+                onPointerDown={() => {
+                    CalendarManager.Instance.open(undefined, docType === 'pin' ? this.pinDoc : this.routeDoc);
+                }}
+                icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />}
+                color={SettingsManager.userColor}
             />
-        )
-       
-    }
+        );
+    };
     addToCalendarButton: JSX.Element = (
-        <IconButton 
-            tooltip="Add to calendar" 
-            onPointerDown={() => CalendarManager.Instance.open(undefined, this.pinDoc)} 
-            icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} 
-            color={SettingsManager.userColor} />
-    )
+        <IconButton tooltip="Add to calendar" onPointerDown={() => CalendarManager.Instance.open(undefined, this.pinDoc)} icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
+    );
 
     getLinkNoteToDocButton = (docType: string): JSX.Element => {
         return (
@@ -335,8 +319,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                     color={SettingsManager.userColor}
                 />
             </div>
-        )
-    }
+        );
+    };
 
     linkNoteToPinOrRoutenButton: JSX.Element = (
         <div ref={this._commentRef}>
@@ -347,16 +331,9 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                 color={SettingsManager.userColor}
             />
         </div>
-    )
-
-    customizePinButton: JSX.Element = (
-        <IconButton 
-            tooltip="Customize pin" 
-            onPointerDown={this.CustomizeClick} 
-            icon={<FontAwesomeIcon icon={faEdit as IconLookup} />} 
-            color={SettingsManager.userColor} 
-        />
-    )
+    );
+
+    customizePinButton: JSX.Element = (<IconButton tooltip="Customize pin" onPointerDown={this.CustomizeClick} icon={<FontAwesomeIcon icon={faEdit as IconLookup} />} color={SettingsManager.userColor} />);
 
     centerOnPinButton: JSX.Element = (
         <IconButton
@@ -365,7 +342,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             icon={<FontAwesomeIcon icon="compress-arrows-alt" />}
             color={SettingsManager.userColor}
         />
-    )
+    );
 
     backButton: JSX.Element = (
         <IconButton
@@ -374,7 +351,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             icon={<FontAwesomeIcon icon={faArrowLeft as IconLookup} />}
             color={SettingsManager.userColor}
         />
-    )
+    );
 
     addRouteButton: JSX.Element = (
         <IconButton
@@ -383,7 +360,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             icon={<FontAwesomeIcon icon={faAdd as IconLookup} />}
             color={SettingsManager.userColor}
         />
-    )
+    );
 
     getDeleteButton = (type: string) => {
         return (
@@ -393,17 +370,10 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                 icon={<FontAwesomeIcon icon="trash-alt" />}
                 color={SettingsManager.userColor}
             />
-        )
-    }
+        );
+    };
 
-    animateRouteButton: JSX.Element = (
-        <IconButton 
-            tooltip="Animate route" 
-            onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} 
-            icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} 
-            color={SettingsManager.userColor} 
-        />
-    )
+    animateRouteButton: JSX.Element = (<IconButton tooltip="Animate route" onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />);
 
     revertToOriginalMarkerButton = (
         <IconButton
@@ -412,7 +382,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             icon={<FontAwesomeIcon icon={faArrowsRotate as IconLookup} />}
             color={SettingsManager.userColor}
         />
-    )
+    );
 
     render() {
         const buttons = (
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 7db139d74..ffd52fb0e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,8 +1,17 @@
+import { IconLookup, faCircleXmark, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Checkbox, FormControlLabel, TextField } from '@mui/material';
+import * as turf from '@turf/turf';
 import { IconButton, Size, Type } from 'browndash-components';
+import * as d3 from 'd3';
+import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, Position } from 'geojson';
+import mapboxgl, { LngLat, LngLatBoundsLike, MapLayerMouseEvent } from 'mapbox-gl';
 import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { CirclePicker, ColorResult } from 'react-color';
+import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl';
+import { MarkerEvent } from 'react-map-gl/dist/esm/types';
 import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
 import { DocCss, Highlight } from '../../../../fields/DocSymbols';
@@ -22,22 +31,13 @@ import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../trails';
-import { MapAnchorMenu } from './MapAnchorMenu';
-import { ControlPosition, Layer, MapProvider, MapRef, Map as MapboxMap, Marker, MarkerProps, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl';
-import './MapBox.scss';
-// import { GeocoderControl } from './GeocoderControl';
-import { IconLookup, faCircleXmark, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
-import { Checkbox, FormControlLabel, TextField } from '@mui/material';
-import * as turf from '@turf/turf';
-import * as d3 from 'd3';
-import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, Position } from 'geojson';
-import mapboxgl, { LngLat, LngLatBoundsLike, MapLayerMouseEvent } from 'mapbox-gl';
-import { CirclePicker, ColorResult } from 'react-color';
-import { MarkerEvent } from 'react-map-gl/dist/esm/types';
 import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons';
 import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility';
+import { MapAnchorMenu } from './MapAnchorMenu';
+import './MapBox.scss';
 import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
 import { MarkerIcons } from './MarkerIcons';
+// import { GeocoderControl } from './GeocoderControl';
 
 // amongus
 /**
@@ -480,7 +480,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     // The pin that is selected
-    @observable selectedPinOrRoute: Doc | undefined;
+    @observable selectedPinOrRoute: Doc | undefined = undefined;
 
     @action
     deselectPinOrRoute = () => {
@@ -650,7 +650,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     deleteSelectedPinOrRoute = undoable(() => {
-        console.log('deleting')
+        console.log('deleting');
         if (this.selectedPinOrRoute) {
             // Removes filter
             Doc.setDocFilter(this.Document, 'latitude', this.selectedPinOrRoute.latitude, 'remove');
@@ -677,15 +677,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.temporaryRouteSource = {
                 type: 'FeatureCollection',
                 features: [],
-            }
-        })
-
+            };
+        });
 
         document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
     };
 
-   
-
     @action
     centerOnSelectedPin = () => {
         if (this.selectedPinOrRoute) {
@@ -882,8 +879,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
             this.temporaryRouteSource = {
                 type: 'FeatureCollection',
-                features: []
-            }
+                features: [],
+            };
             MapAnchorMenu.Instance.fadeOut(true);
             return mapRoute;
         }
@@ -994,8 +991,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
 
-
-
     /**
      * Makes a reverse geocoding API call to retrieve features corresponding to a map click (based on longitude
      * and latitude). Sets the search results accordingly.
@@ -1388,7 +1383,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
 
-
     getRouteAnimationOptions = (): JSX.Element => {
         return (
             <>
@@ -1423,20 +1417,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 <>
                     <div className="animation-suboptions">
                         <div>|</div>
-                        <FormControlLabel
-                            className="first-person-label"
-                            label="1st person animation:"
-                            labelPlacement="start"
-                            control={<Checkbox color="success" checked={this.isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />}
-                        />
+                        <FormControlLabel className="first-person-label" label="1st person animation:" labelPlacement="start" control={<Checkbox color="success" checked={this.isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />} />
                         <div id="divider">|</div>
                         <IconButton tooltip={this.animationSpeedTooltipText} onPointerDown={this.updateAnimationSpeed} icon={this.animationSpeedIcon} size={Size.MEDIUM} />
                         <div id="divider">|</div>
-                        <div style={{display: 'flex', alignItems: 'center'}}>
+                        <div style={{ display: 'flex', alignItems: 'center' }}>
                             <div>Select Line Color: </div>
-                            <CirclePicker circleSize={12} circleSpacing={5} width="100%" colors={['#ffff00', '#03a9f4', '#ff0000', '#ff5722', '#000000', '#673ab7']} onChange={(color:any) => this.setAnimationLineColor(color)} />
+                            <CirclePicker circleSize={12} circleSpacing={5} width="100%" colors={['#ffff00', '#03a9f4', '#ff0000', '#ff5722', '#000000', '#673ab7']} onChange={(color: any) => this.setAnimationLineColor(color)} />
                         </div>
-                        
                     </div>
                 </>
             </>
@@ -1478,7 +1466,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         const bearing = parseInt(e.target.value);
         if (!isNaN(bearing) && this._mapRef.current) {
-            console.log('bearing change')
+            console.log('bearing change');
             const fixedBearing = Math.max(0, Math.min(360, bearing));
             this._mapRef.current.setBearing(fixedBearing);
             this.dataDoc.map_bearing = fixedBearing;
@@ -1489,7 +1477,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         const pitch = parseInt(e.target.value);
         if (!isNaN(pitch) && this._mapRef.current) {
-            console.log('pitch change')
+            console.log('pitch change');
             const fixedPitch = Math.max(0, Math.min(85, pitch));
             this._mapRef.current.setPitch(fixedPitch);
             this.dataDoc.map_pitch = fixedPitch;
@@ -1648,7 +1636,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         <MapboxMap
                             ref={this._mapRef}
                             mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
-                            viewState={(this.isAnimating || this.routeToAnimate) ? undefined : { ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width), height: NumCast(this.layoutDoc._height) }}
+                            viewState={this.isAnimating || this.routeToAnimate ? undefined : { ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width), height: NumCast(this.layoutDoc._height) }}
                             mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'}
                             style={{
                                 position: 'absolute',
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index fdd8285d5..1dbbbb633 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -87,7 +87,7 @@
 //     private _dropDisposer?: DragManager.DragDropDisposer;
 //     private _disposers: { [name: string]: IReactionDisposer } = {};
 //     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
-//     @observable private _overlayAnnoInfo: Opt<Doc>;
+//     @observable private _overlayAnnoInfo: Opt<Doc> = undefined;
 //     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
 //     public static LayoutString(fieldKey: string) {
 //         return FieldView.LayoutString(MapBox2, fieldKey);
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index 6a14427c0..70037f29c 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -1,33 +1,31 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import BingMapsReact from 'bingmaps-react';
 import { Button, EditableText, IconButton, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { MapProvider, Map as MapboxMap } from 'react-map-gl';
+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 { Id } from '../../../../fields/FieldSymbols';
 import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { 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 { SnappingManager } from '../../../util/SnappingManager';
 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 { MapAnchorMenu } from '../MapBox/MapAnchorMenu';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../trails';
 import './MapBox.scss';
-import { MapAnchorMenu } from '../MapBox/MapAnchorMenu';
-import { MapProvider, Map as MapboxMap } from 'react-map-gl';
 
 // amongus
 /**
@@ -351,7 +349,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
     }, 'createpin');
 
     // The pin that is selected
-    @observable selectedPin: Doc | undefined;
+    @observable selectedPin: Doc | undefined = undefined;
 
     @action
     deselectPin = () => {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 733febd2d..7f1d6b049 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -48,7 +48,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _sidebarRef = React.createRef<SidebarAnnos>();
 
     @observable private _searching: boolean = false;
-    @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+    @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy> = undefined;
     @observable private _pageControls = false;
 
     @computed get pdfUrl() {
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
index e75b1ab6f..135db64e0 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -6,8 +6,9 @@ import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
 import ReplayIcon from '@mui/icons-material/Replay';
 import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControl, FormControlLabel, FormGroup, IconButton, LinearProgress, Stack } from '@mui/material';
 import Typography from '@mui/material/Typography';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { NumListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
@@ -19,7 +20,6 @@ import questions from './PhysicsSimulationQuestions.json';
 import tutorials from './PhysicsSimulationTutorial.json';
 import Wall from './PhysicsSimulationWall';
 import Weight from './PhysicsSimulationWeight';
-import * as React from 'react';
 
 interface IWallProps {
     length: number;
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index f5077a07e..3b232ddd0 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -1,7 +1,7 @@
 import { computed, IReactionDisposer, makeObservable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import './PhysicsSimulationBox.scss';
 import * as React from 'react';
+import './PhysicsSimulationBox.scss';
 
 interface IWallProps {
     length: number;
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index 061a46f03..3b2fc033d 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -1,6 +1,6 @@
-import * as React from 'react';
 import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import './RadialMenu.scss';
 import { RadialMenuItem, RadialMenuProps } from './RadialMenuItem';
 
diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx
index c931202f1..10a90befd 100644
--- a/src/client/views/nodes/RadialMenuItem.tsx
+++ b/src/client/views/nodes/RadialMenuItem.tsx
@@ -1,7 +1,7 @@
-import * as React from 'react';
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { UndoManager } from '../../util/UndoManager';
 
 export interface RadialMenuProps {
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index f01642236..658cfb1ca 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -8,15 +8,15 @@ import { List } from '../../../../fields/List';
 import { BoolCast, DocCast } from '../../../../fields/Types';
 import { VideoField } from '../../../../fields/URLField';
 import { Upload } from '../../../../server/SharedMediaTypes';
-import { Docs } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
+import { Docs } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { Presentation } from '../../../util/TrackMovements';
 import { undoBatch } from '../../../util/UndoManager';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
 import { ViewBoxBaseComponent } from '../../DocComponent';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
 import { media_state } from '../AudioBox';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { VideoBox } from '../VideoBox';
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index d5d31b407..79ed69cdd 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,5 +1,5 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import * as React from 'react';
 // import { Canvas } from '@react-three/fiber';
 import { computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 7e7eaee45..8e506ec64 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -2,25 +2,23 @@ let ReactTextareaAutocomplete = require('@webscopeio/react-textarea-autocomplete
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { returnAlways, returnEmptyString } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { returnAlways, returnEmptyString, returnTrue } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
-import { InteractionUtils } from '../../util/InteractionUtils';
+import { ScriptManager } from '../../util/ScriptManager';
 import { CompileScript, ScriptParam } from '../../util/Scripting';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { ScriptManager } from '../../util/ScriptManager';
 import { ContextMenu } from '../ContextMenu';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { EditableView } from '../EditableView';
-import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { OverlayView } from '../OverlayView';
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { DocumentIconContainer } from './DocumentIcon';
-import { DocFocusOptions, DocumentView } from './DocumentView';
 import './ScriptingBox.scss';
 const _global = (window /* browser */ || global) /* node */ as any;
 
diff --git a/src/client/views/nodes/TaskCompletedBox.tsx b/src/client/views/nodes/TaskCompletedBox.tsx
index 9aab8c5a9..c9e15d314 100644
--- a/src/client/views/nodes/TaskCompletedBox.tsx
+++ b/src/client/views/nodes/TaskCompletedBox.tsx
@@ -1,15 +1,15 @@
-import * as React from 'react';
+import { Fade } from '@mui/material';
+import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import './TaskCompletedBox.scss';
-import { observable, action } from 'mobx';
-import { Fade } from '@mui/material';
 
 @observer
 export class TaskCompletionBox extends React.Component<{}> {
     @observable public static taskCompleted: boolean = false;
     @observable public static popupX: number = 500;
     @observable public static popupY: number = 150;
-    @observable public static textDisplayed: string;
+    @observable public static textDisplayed: string = '';
 
     @action
     public static toggleTaskCompleted = () => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index f205dbd56..8e9cfe3d7 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -74,8 +74,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._props.setContentView?.(this);
     }
 
-    @observable _stackedTimeline: any; // CollectionStackedTimeline ref
-    @observable static _nativeControls: boolean; // default html controls
+    @observable _stackedTimeline: any = undefined; // CollectionStackedTimeline ref
+    @observable static _nativeControls: boolean = false; // default html controls
     @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable _screenCapture = false;
     @observable _clicking = false; // used for transition between showing/hiding timeline
@@ -86,7 +86,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @observable _finished: boolean = false; // has playback reached end of clip
     @observable _volume: number = 1;
     @observable _muted: boolean = false;
-    @observable _controlsTransform?: { X: number; Y: number };
+    @observable _controlsTransform?: { X: number; Y: number } = undefined;
     @observable _controlsVisible: boolean = true;
     @observable _scrubbing: boolean = false;
 
diff --git a/src/client/views/nodes/audio/AudioWaveform.tsx b/src/client/views/nodes/audio/AudioWaveform.tsx
index c034d95ea..3e46ff4e6 100644
--- a/src/client/views/nodes/audio/AudioWaveform.tsx
+++ b/src/client/views/nodes/audio/AudioWaveform.tsx
@@ -7,10 +7,10 @@ import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { Cast } from '../../../../fields/Types';
 import { numberRange } from '../../../../Utils';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { Colors } from './../../global/globalEnums';
 import './AudioWaveform.scss';
 import { WaveCanvas } from './WaveCanvas';
-import { ObservableReactComponent } from '../../ObservableReactComponent';
 
 /**
  * AudioWaveform
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 989feb774..4fb1a7f17 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -1,40 +1,35 @@
-import * as React from 'react';
-import { observer } from "mobx-react";
-import { Doc } from "../../../../fields/Doc";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps, ViewBoxBaseComponent } from '../../DocComponent';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { StrCast } from '../../../../fields/Types';
+import { Calendar, EventClickArg, EventSourceInput } from '@fullcalendar/core';
+import dayGridPlugin from '@fullcalendar/daygrid';
+import multiMonthPlugin from '@fullcalendar/multimonth';
 import { makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import { dateRangeStrToDates } from '../../../../Utils';
-import { Calendar, EventClickArg, EventSourceInput } from '@fullcalendar/core'
-import dayGridPlugin from '@fullcalendar/daygrid'
-import multiMonthPlugin from '@fullcalendar/multimonth'
-import { faListNumeric } from '@fortawesome/free-solid-svg-icons';
+import { Doc } from '../../../../fields/Doc';
+import { StrCast } from '../../../../fields/Types';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { FieldView, FieldViewProps } from '../FieldView';
 
 type CalendarView = 'month' | 'multi-month' | 'week';
 
 @observer
-export class CalendarBox extends ViewBoxBaseComponent<FieldViewProps>(){
+export class CalendarBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string = 'calendar') {
         return FieldView.LayoutString(CalendarBox, fieldKey);
     }
 
-    componentDidMount(): void {
-        
-    }
+    componentDidMount(): void {}
 
-    componentWillUnmount(): void {
-        
-    }
+    componentWillUnmount(): void {}
 
-    _calendarRef = React.createRef<HTMLElement>()
+    _calendarRef = React.createRef<HTMLElement>();
 
-    get dateRangeStr (){
+    get dateRangeStr() {
         return StrCast(this.Document.date_range);
     }
 
-    // Choose a calendar view based on the date range 
-    get calendarViewType (): CalendarView {
+    // Choose a calendar view based on the date range
+    get calendarViewType(): CalendarView {
         const [fromDate, toDate] = dateRangeStrToDates(this.dateRangeStr);
 
         if (fromDate.getFullYear() !== toDate.getFullYear() || fromDate.getMonth() !== toDate.getMonth()) return 'multi-month';
@@ -43,92 +38,83 @@ export class CalendarBox extends ViewBoxBaseComponent<FieldViewProps>(){
         return 'week';
     }
 
-    get calendarStartDate () {
-        return this.dateRangeStr.split("|")[0];
+    get calendarStartDate() {
+        return this.dateRangeStr.split('|')[0];
     }
 
-    get calendarToDate () {
-        return this.dateRangeStr.split("|")[1];
+    get calendarToDate() {
+        return this.dateRangeStr.split('|')[1];
     }
 
-    get childDocs (): Doc[] {
+    get childDocs(): Doc[] {
         return this.childDocs; // get all sub docs for a calendar
     }
 
-    docBackgroundColor (type: string): string {
+    docBackgroundColor(type: string): string {
         // TODO: Return a different color based on the event type
         return 'blue';
     }
 
-    get calendarEvents (): EventSourceInput | undefined {
+    get calendarEvents(): EventSourceInput | undefined {
         if (this.childDocs.length === 0) return undefined;
         return this.childDocs.map((doc, idx) => {
             const docTitle = StrCast(doc.title);
             const docDateRange = StrCast(doc.date_range);
             const [startDate, endDate] = dateRangeStrToDates(docDateRange);
             const docType = doc.type;
-            const docDescription = doc.description ? StrCast(doc.description): "";
+            const docDescription = doc.description ? StrCast(doc.description) : '';
 
             return {
                 title: docTitle,
                 start: startDate,
                 end: endDate,
                 allDay: false,
-                classNames:[StrCast(docType)], // will determine the style 
+                classNames: [StrCast(docType)], // will determine the style
                 editable: false, // subject to change in the future
                 backgroundColor: this.docBackgroundColor(StrCast(doc.type)),
                 color: 'white',
                 extendedProps: {
-                    description: docDescription
+                    description: docDescription,
                 },
-                
-            }
-
-        })
+            };
+        });
     }
 
     handleEventClick = (arg: EventClickArg) => {
         // TODO: open popover with event description, option to open CalendarManager and change event date, delete event, etc.
-    }
+    };
 
     calendarEl: HTMLElement = document.getElementById('calendar-box-v1')!;
 
     // https://fullcalendar.io
     get calendar() {
-        return new Calendar(this.calendarEl, 
-            {
-                plugins: [this.calendarViewType === 'multi-month' ? multiMonthPlugin : dayGridPlugin],
-                headerToolbar: {
-                    left: 'prev,next today',
-                    center: 'title',
-                    right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
-                },
-                initialDate: this.calendarStartDate,
-                navLinks: true,
-                editable: false,
-                displayEventTime: false,
-                displayEventEnd: false,
-                events: this.calendarEvents,
-                eventClick: this.handleEventClick
-            }
-        )
-        
+        return new Calendar(this.calendarEl, {
+            plugins: [this.calendarViewType === 'multi-month' ? multiMonthPlugin : dayGridPlugin],
+            headerToolbar: {
+                left: 'prev,next today',
+                center: 'title',
+                right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek',
+            },
+            initialDate: this.calendarStartDate,
+            navLinks: true,
+            editable: false,
+            displayEventTime: false,
+            displayEventEnd: false,
+            events: this.calendarEvents,
+            eventClick: this.handleEventClick,
+        });
     }
 
-
-    constructor(props: any){
+    constructor(props: any) {
         super(props);
         makeObservable(this);
     }
 
-    render(){
+    render() {
         return (
-            <div className='calendar-box-conatiner'>
-                <div id='calendar-box-v1'>
-
-                </div>
-            </div>  
+            <div className="calendar-box-conatiner">
+                <div id="calendar-box-v1"></div>
+            </div>
         );
-        
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 6332b200d..1002ee403 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,17 +1,17 @@
 import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { NodeSelection } from 'prosemirror-state';
+import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
 import { Height, Width } from '../../../../fields/DocSymbols';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnTrue, Utils } from '../../../../Utils';
+import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { Transform } from '../../../util/Transform';
 import { DocFocusOptions, DocumentView } from '../DocumentView';
 import { FormattedTextBox } from './FormattedTextBox';
-import * as React from 'react';
 
 export class DashDocView {
     dom: HTMLSpanElement; // container for label and value
@@ -83,7 +83,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
     _disposers: { [name: string]: IReactionDisposer } = {};
     _textBox: FormattedTextBox;
     @observable _dashDoc: Doc | undefined = undefined;
-    @observable _finalLayout: any;
+    @observable _finalLayout: any = undefined;
     @observable _width: number = 0;
     @observable _height: number = 0;
 
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 331ed1980..7e655531e 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -1,14 +1,13 @@
-import EquationEditor from './EquationEditor';
-import { IReactionDisposer, trace } from 'mobx';
+import { IReactionDisposer } from 'mobx';
 import { observer } from 'mobx-react';
 import { TextSelection } from 'prosemirror-state';
+import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
 import { StrCast } from '../../../../fields/Types';
 import './DashFieldView.scss';
+import EquationEditor from './EquationEditor';
 import { FormattedTextBox } from './FormattedTextBox';
-import * as React from 'react';
-import { AnyArray } from 'mongoose';
 
 export class EquationView {
     dom: HTMLDivElement; // container for label and value
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ad2fab8b0..8bf8abafa 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -11,6 +11,7 @@ import { keymap } from 'prosemirror-keymap';
 import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
+import * as React from 'react';
 import { BsMarkdownFill } from 'react-icons/bs';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
@@ -70,9 +71,8 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
 import { RichTextRules } from './RichTextRules';
 import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
-// import * as applyDevTools from 'prosemirror-dev-tools';
-import * as React from 'react';
 export const GoogleRef = 'googleDocId';
+// import * as applyDevTools from 'prosemirror-dev-tools';
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
 export interface FormattedTextBoxProps {}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 4881070fd..3b31f2d17 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -32,7 +32,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     private _linkToRef = React.createRef<HTMLInputElement>();
 
     layoutDoc: Doc | undefined;
-    @observable public view?: EditorView;
+    @observable public view?: EditorView = undefined;
     public editorProps: FieldViewProps | undefined;
 
     public _brushMap: Map<string, Set<Mark>> = new Map();
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index 9dc0c5180..b573f7c48 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -1,13 +1,11 @@
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnFalse } from '../../../../Utils';
 import { Doc } from '../../../../fields/Doc';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../../Utils';
-import { Transform } from '../../../util/Transform';
 import { ViewBoxBaseComponent } from '../../DocComponent';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { DocumentView, DocumentViewInternal } from '../DocumentView';
+import { DocumentView } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import * as React from 'react';
 
 @observer
 export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index e213b2fc5..0305689e7 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,8 +1,8 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
 import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast, FieldResult, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { Animation } from '../../../../fields/DocSymbols';
 import { Copy, Id } from '../../../../fields/FieldSymbols';
@@ -24,7 +24,7 @@ import { SerializationHelper } from '../../../util/SerializationHelper';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
-import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from '../../collections/collectionFreeForm';
+import { CollectionFreeFormView, MarqueeViewBounds } from '../../collections/collectionFreeForm';
 import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
 import { CollectionView } from '../../collections/CollectionView';
 import { TreeView } from '../../collections/TreeView';
@@ -83,12 +83,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     _batch: UndoManager.Batch | undefined = undefined; // undo batch for dragging sliders which generate multiple scene edit events as the cursor moves
     _keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed.  Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation.
     _unmounting = false; // flag that view is unmounting used to block RemFromMap from deleting things
+    _presTimer: NodeJS.Timeout | undefined;
 
     @observable public static Instance: PresBox;
 
     @observable _isChildActive = false;
     @observable _moveOnFromAudio: boolean = true;
-    @observable _presTimer!: NodeJS.Timeout;
 
     @observable _eleArray: HTMLElement[] = [];
     @observable _dragArray: HTMLElement[] = [];
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index cf19ff6bc..5ea9e9979 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,18 +1,17 @@
-import * as React from 'react';
 import { action, computed } 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 { LinkFollower } from '../../util/LinkFollower';
+import { LinkManager } from '../../util/LinkManager';
 import { undoBatch } from '../../util/UndoManager';
 import { OpenWhere } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { AnchorMenu } from './AnchorMenu';
 import './Annotation.scss';
-import { LinkManager } from '../../util/LinkManager';
-import { Rect } from 'react-measure';
 
 interface IAnnotationProps extends FieldViewProps {
     anno: Doc;
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index db21d9a3d..da8a88803 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,20 +1,20 @@
-import * as React from 'react';
-import './GPTPopup.scss';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, IconButton, Type } from 'browndash-components';
 import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { CgClose } from 'react-icons/cg';
 import ReactLoading from 'react-loading';
 import { TypeAnimation } from 'react-type-animation';
+import { Utils } from '../../../../Utils';
 import { Doc } from '../../../../fields/Doc';
-import { DocUtils, Docs } from '../../../documents/Documents';
-import { Button, IconButton, Type } from 'browndash-components';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { CgClose } from 'react-icons/cg';
-import { AnchorMenu } from '../AnchorMenu';
-import { gptImageCall } from '../../../apis/gpt/GPT';
 import { Networking } from '../../../Network';
-import { Utils } from '../../../../Utils';
+import { gptImageCall } from '../../../apis/gpt/GPT';
+import { DocUtils, Docs } from '../../../documents/Documents';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { AnchorMenu } from '../AnchorMenu';
+import './GPTPopup.scss';
 
 export enum GPTPopupMode {
     SUMMARY,
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index e342c25b3..b6d027d30 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -302,7 +302,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
         }
     };
 
-    @observable private _scrollTimer: any;
+    @observable private _scrollTimer: any = undefined;
 
     onScroll = (e: React.UIEvent<HTMLElement>) => {
         if (this._mainCont.current && !this._forcedScroll) {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index ccfccb771..14187833f 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -2,24 +2,23 @@ import { Tooltip } from '@mui/material';
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../../fields/Doc';
+import { Doc, DocListCastAsync, Field } from '../../../fields/Doc';
 import { DirectLinks } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { DocCast, StrCast } from '../../../fields/Types';
-import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
+import { DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { LinkManager } from '../../util/LinkManager';
+import { SearchUtil } from '../../util/SearchUtil';
+import { SettingsManager } from '../../util/SettingsManager';
 import { undoBatch } from '../../util/UndoManager';
-import { CollectionDockingView } from '../collections/CollectionDockingView';
 import { ViewBoxBaseComponent } from '../DocComponent';
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { IRecommendation, Recommendation } from '../newlightbox/components';
+import { fetchRecommendations } from '../newlightbox/utils';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import './SearchBox.scss';
-import { fetchRecommendations } from '../newlightbox/utils';
-import { IRecommendation, Recommendation } from '../newlightbox/components';
-import { Colors } from '../global/globalEnums';
-import { SettingsManager } from '../../util/SettingsManager';
-import { SearchUtil } from '../../util/SearchUtil';
 
 const DAMPENING_FACTOR = 0.9;
 const MAX_ITERATIONS = 25;
diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx
index 39e778b76..daacb368b 100644
--- a/src/client/views/selectedDoc/SelectedDocView.tsx
+++ b/src/client/views/selectedDoc/SelectedDocView.tsx
@@ -1,14 +1,14 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { ListBox } from 'browndash-components';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { StrCast } from '../../../fields/Types';
 import { DocumentManager } from '../../util/DocumentManager';
-import { DocFocusOptions } from '../nodes/DocumentView';
-import { emptyFunction } from '../../../Utils';
 import { SettingsManager } from '../../util/SettingsManager';
+import { DocFocusOptions } from '../nodes/DocumentView';
 
 export interface SelectedDocViewProps {
     selectedDocs: Doc[];
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0f3487cd1..575d5849e 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -16,15 +16,15 @@ import { ReportManager } from '../../util/reportManager/ReportManager';
 import { ServerStats } from '../../util/ServerStats';
 import { SettingsManager } from '../../util/SettingsManager';
 import { SharingManager } from '../../util/SharingManager';
+import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { CollectionDockingView } from '../collections/CollectionDockingView';
 import { CollectionLinearView } from '../collections/collectionLinear';
 import { DashboardView } from '../DashboardView';
 import { Colors } from '../global/globalEnums';
-import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
+import { DocumentViewInternal } from '../nodes/DocumentView';
 import { DefaultStyleProvider } from '../StyleProvider';
 import './TopBar.scss';
-import { SnappingManager } from '../../util/SnappingManager';
 
 /**
  * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index f1739a41a..94458563e 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -3,16 +3,15 @@ import { faPhoneSlash, faSync } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
+import { SnappingManager } from '../../util/SnappingManager';
 import '../../views/nodes/WebBox.scss';
 import { CollectionFreeFormDocumentViewProps } from '../nodes/CollectionFreeFormDocumentView';
-import { DocumentView } from '../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import './DashWebRTCVideo.scss';
 import { hangup, initialize, refreshVideos } from './WebCamLogic';
-import * as React from 'react';
-import { SnappingManager } from '../../util/SnappingManager';
 
 /**
  * This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index b8081648f..a9f7c085f 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -1,13 +1,13 @@
+import { computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
-import { observer } from 'mobx-react';
-import { observable, computed } from 'mobx';
+import { DocServer } from '../client/DocServer';
+import { resolvedPorts } from '../client/util/CurrentUserUtils';
 import { CompileScript } from '../client/util/Scripting';
-import { makeInterface } from '../fields/Schema';
 import { ObjectField } from '../fields/ObjectField';
 import { RefField } from '../fields/RefField';
-import { DocServer } from '../client/DocServer';
-import { resolvedPorts } from '../client/util/CurrentUserUtils';
+import { makeInterface } from '../fields/Schema';
 
 @observer
 class Repl extends React.Component {
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index 02038c426..f46adef77 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -1,19 +1,19 @@
-import { action, configure, observable, ObservableMap, Lambda } from 'mobx';
+import { action, configure, observable } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
-import { observer } from 'mobx-react';
-import { Doc, Field, FieldResult, Opt } from '../fields/Doc';
 import { DocServer } from '../client/DocServer';
+import { resolvedPorts } from '../client/util/CurrentUserUtils';
+import { CompileScript } from '../client/util/Scripting';
+import { EditableView } from '../client/views/EditableView';
+import CursorField from '../fields/CursorField';
+import { DateField } from '../fields/DateField';
+import { Doc, Field, FieldResult } from '../fields/Doc';
 import { Id } from '../fields/FieldSymbols';
 import { List } from '../fields/List';
-import { URLField } from '../fields/URLField';
-import { EditableView } from '../client/views/EditableView';
-import { CompileScript } from '../client/util/Scripting';
 import { RichTextField } from '../fields/RichTextField';
-import { DateField } from '../fields/DateField';
 import { ScriptField } from '../fields/ScriptField';
-import CursorField from '../fields/CursorField';
-import { resolvedPorts } from '../client/util/CurrentUserUtils';
+import { URLField } from '../fields/URLField';
 
 DateField;
 URLField;
diff --git a/src/fields/CursorField.ts b/src/fields/CursorField.ts
index 46f5a8e1c..84917ae53 100644
--- a/src/fields/CursorField.ts
+++ b/src/fields/CursorField.ts
@@ -1,8 +1,7 @@
-import { ObjectField } from './ObjectField';
-import { observable } from 'mobx';
+import { createSimpleSchema, object, serializable } from 'serializr';
 import { Deserializable } from '../client/util/SerializationHelper';
-import { serializable, createSimpleSchema, object, date } from 'serializr';
-import { FieldChanged, ToScriptString, ToString, Copy } from './FieldSymbols';
+import { Copy, FieldChanged, ToScriptString, ToString } from './FieldSymbols';
+import { ObjectField } from './ObjectField';
 
 export type CursorPosition = {
     x: number;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 8903a9f97..9dc3c173d 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -265,8 +265,8 @@ export class Doc extends RefField {
     @observable public [DocAcl]: { [key: string]: symbol } = {};
     @observable public [DocCss]: number = 0; // incrementer denoting a change to CSS layout
     @observable public [DirectLinks] = new ObservableSet<Doc>();
-    @observable public [AudioPlay]: any; // meant to store sound object from Howl
-    @observable public [Animation]: Opt<Doc>;
+    @observable public [AudioPlay]: any = undefined; // meant to store sound object from Howl
+    @observable public [Animation]: Opt<Doc> = undefined;
     @observable public [Highlight]: boolean = false;
     @observable public [Brushed]: boolean = false;
     @observable public [DocViews] = new ObservableSet<DocumentView>();
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 8c8ff1ea3..b8ad552d2 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -2,7 +2,7 @@ import { action, computed, makeObservable, observable } from 'mobx';
 import { alias, list, serializable } from 'serializr';
 import { DocServer } from '../client/DocServer';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
-import { afterDocDeserialize, autoObject, Deserializable } from '../client/util/SerializationHelper';
+import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper';
 import { Field } from './Doc';
 import { FieldTuples, Self, SelfProxy } from './DocSymbols';
 import { Copy, FieldChanged, Parent, ToScriptString, ToString } from './FieldSymbols';
@@ -11,7 +11,7 @@ import { ProxyField } from './Proxy';
 import { RefField } from './RefField';
 import { listSpec } from './Schema';
 import { Cast } from './Types';
-import { deleteProperty, getter, setter, containedFieldChangedHandler } from './util';
+import { containedFieldChangedHandler, deleteProperty, getter, setter } from './util';
 
 function toObjectField(field: Field) {
     return field instanceof RefField ? new ProxyField(field) : field;
@@ -102,8 +102,8 @@ class ListImpl<T extends Field> extends ObjectField {
                 items.length === 0 && deleteCount
                     ? { op: '$remFromSet', items: removed, hint: { start, deleteCount }, length: list.__fieldTuples.length }
                     : items.length && !deleteCount && start === list.__fieldTuples.length
-                    ? { op: '$addToSet', items, length: list.__fieldTuples.length }
-                    : undefined
+                      ? { op: '$addToSet', items, length: list.__fieldTuples.length }
+                      : undefined
             );
             return res.map(toRealField);
         }),
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index c076f5fe1..3a46e3581 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -1,12 +1,12 @@
-import { Deserializable } from '../client/util/SerializationHelper';
-import { Field, FieldWaiting, Opt } from './Doc';
+import { action, computed, observable, runInAction } from 'mobx';
 import { primitive, serializable } from 'serializr';
-import { observable, action, runInAction, computed } from 'mobx';
 import { DocServer } from '../client/DocServer';
-import { RefField } from './RefField';
-import { ObjectField } from './ObjectField';
-import { Id, Copy, ToScriptString, ToString, ToValue } from './FieldSymbols';
 import { scriptingGlobal } from '../client/util/ScriptingGlobals';
+import { Deserializable } from '../client/util/SerializationHelper';
+import { Field, FieldWaiting, Opt } from './Doc';
+import { Copy, Id, ToScriptString, ToString, ToValue } from './FieldSymbols';
+import { ObjectField } from './ObjectField';
+import { RefField } from './RefField';
 
 function deserializeProxy(field: any) {
     if (!field.cache.field) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 545fe4478..b73520999 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,11 +1,11 @@
 import { $mobx, action, observable, runInAction, trace } from 'mobx';
 import { computedFn } from 'mobx-utils';
+import { returnZero } from '../Utils';
 import { DocServer } from '../client/DocServer';
 import { LinkManager } from '../client/util/LinkManager';
 import { SerializationHelper } from '../client/util/SerializationHelper';
 import { UndoManager } from '../client/util/UndoManager';
-import { returnZero } from '../Utils';
-import { aclLevel, Doc, DocListCast, Field, FieldResult, HierarchyMapping, ReverseHierarchyMap, StrListCast, updateCachedAcls } from './Doc';
+import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, ReverseHierarchyMap, StrListCast, aclLevel, updateCachedAcls } from './Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, UpdatingFromServer, Width } from './DocSymbols';
 import { FieldChanged, Id, Parent, ToValue } from './FieldSymbols';
 import { List } from './List';
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index d2598c2db..e333e6a2e 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -1,19 +1,19 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import * as rp from 'request-promise';
+import { Utils } from '../Utils';
 import { DocServer } from '../client/DocServer';
-import { Docs } from '../client/documents/Documents';
 import { Networking } from '../client/Network';
+import { Docs } from '../client/documents/Documents';
 import { MainViewModal } from '../client/views/MainViewModal';
 import { Doc, Opt } from '../fields/Doc';
 import { List } from '../fields/List';
 import { listSpec } from '../fields/Schema';
 import { Cast } from '../fields/Types';
-import { Utils } from '../Utils';
 import './ImageUpload.scss';
 import { MobileInterface } from './MobileInterface';
-import * as React from 'react';
 const { default: { DFLT_IMAGE_NATIVE_DIM } } = require('../client/views/global/globalCssVariables.module.scss'); // prettier-ignore
 export interface ImageUploadProps {
     Document: Doc; // Target document for upload (upload location)
diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx
index 2e595e4bc..23e19585a 100644
--- a/src/mobile/MobileInkOverlay.tsx
+++ b/src/mobile/MobileInkOverlay.tsx
@@ -1,6 +1,6 @@
-import * as React from 'react';
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { DocServer } from '../client/DocServer';
 import { DragManager } from '../client/util/DragManager';
 import { Doc } from '../fields/Doc';
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index bdd657575..e3d8f9394 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -1,52 +1,33 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import {
-    faTasks,
-    faReply,
-    faQuoteLeft,
-    faHandPointLeft,
-    faFolderOpen,
+    faAddressCard,
+    faAlignLeft,
+    faAlignRight,
     faAngleDoubleLeft,
-    faExternalLinkSquareAlt,
-    faMobile,
-    faThLarge,
-    faWindowClose,
-    faEdit,
-    faTrashAlt,
-    faPalette,
     faAngleRight,
+    faArrowDown,
+    faArrowLeft,
+    faArrowRight,
+    faArrowUp,
+    faArrowsAltH,
+    faAsterisk,
+    faBars,
     faBell,
-    faTrash,
+    faBolt,
+    faBook,
+    faBrain,
+    faBullseye,
+    faCalculator,
     faCamera,
-    faExpand,
     faCaretDown,
     faCaretLeft,
     faCaretRight,
     faCaretSquareDown,
     faCaretSquareRight,
-    faArrowsAltH,
-    faPlus,
-    faMinus,
-    faTerminal,
-    faToggleOn,
-    faFile as fileSolid,
-    faExternalLinkAlt,
-    faLocationArrow,
-    faSearch,
-    faFileDownload,
-    faStop,
-    faCalculator,
-    faWindowMaximize,
-    faAddressCard,
-    faQuestionCircle,
-    faArrowLeft,
-    faArrowRight,
-    faArrowDown,
-    faArrowUp,
-    faBolt,
-    faBullseye,
     faCaretUp,
     faCat,
     faCheck,
+    faChevronLeft,
     faChevronRight,
     faClipboard,
     faClone,
@@ -54,76 +35,95 @@ import {
     faCommentAlt,
     faCompressArrowsAlt,
     faCut,
+    faEdit,
     faEllipsisV,
     faEraser,
     faExclamation,
+    faExpand,
+    faExternalLinkAlt,
+    faExternalLinkSquareAlt,
+    faEye,
     faFileAlt,
     faFileAudio,
+    faFileDownload,
     faFilePdf,
     faFilm,
     faFilter,
+    faFolderOpen,
     faFont,
     faGlobeAsia,
+    faHandPointLeft,
     faHighlighter,
+    faHome,
+    faImage,
+    faLocationArrow,
+    faLongArrowAltLeft,
     faLongArrowAltRight,
     faMicrophone,
+    faMinus,
+    faMobile,
     faMousePointer,
     faMusic,
     faObjectGroup,
+    faPaintBrush,
+    faPalette,
     faPause,
     faPen,
     faPenNib,
     faPhone,
     faPlay,
+    faPlus,
     faPortrait,
+    faQuestionCircle,
+    faQuoteLeft,
     faRedoAlt,
+    faReply,
+    faSearch,
     faStamp,
     faStickyNote,
+    faStop,
+    faTasks,
+    faTerminal,
+    faTh,
+    faThLarge,
     faThumbtack,
+    faTimes,
+    faToggleOn,
+    faTrash,
+    faTrashAlt,
     faTree,
     faTv,
-    faBook,
     faUndoAlt,
     faVideo,
-    faAsterisk,
-    faBrain,
-    faImage,
-    faPaintBrush,
-    faTimes,
-    faEye,
-    faHome,
-    faLongArrowAltLeft,
-    faBars,
-    faTh,
-    faChevronLeft,
-    faAlignRight,
-    faAlignLeft,
+    faWindowClose,
+    faWindowMaximize,
+    faFile as fileSolid,
 } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../Utils';
 import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes';
+import { Docs, DocumentOptions } from '../client/documents/Documents';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
-import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
+import { SettingsManager } from '../client/util/SettingsManager';
 import { Transform } from '../client/util/Transform';
 import { UndoManager } from '../client/util/UndoManager';
-import { TabDocView } from '../client/views/collections/TabDocView';
 import { GestureOverlay } from '../client/views/GestureOverlay';
+import { TabDocView } from '../client/views/collections/TabDocView';
 import { AudioBox } from '../client/views/nodes/AudioBox';
 import { DocumentView } from '../client/views/nodes/DocumentView';
-import { RichTextMenu } from '../client/views/nodes/formattedText/RichTextMenu';
 import { RadialMenu } from '../client/views/nodes/RadialMenu';
+import { RichTextMenu } from '../client/views/nodes/formattedText/RichTextMenu';
 import { Doc, DocListCast } from '../fields/Doc';
 import { InkTool } from '../fields/InkField';
 import { List } from '../fields/List';
 import { ScriptField } from '../fields/ScriptField';
 import { Cast, FieldValue, StrCast } from '../fields/Types';
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero } from '../Utils';
-import { Uploader } from './ImageUpload';
 import './AudioUpload.scss';
+import { Uploader } from './ImageUpload';
 import './ImageUpload.scss';
 import './MobileInterface.scss';
 
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 8f21966ae..d46977816 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -14,9 +14,7 @@ declare module 'pdfjs-dist/web/pdf_viewer';
 declare module 'react-jsx-parser';
 
 declare module 'express-flash';
-declare module 'connect-flash' {
-    interface flash {}
-}
+declare module 'connect-flash';
 declare module 'connect-mongo';
 declare module '@mui/material';
 
diff --git a/test/test.ts b/test/test.ts
index 489aa3025..ee14a9340 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -1,37 +1,41 @@
 import { expect } from 'chai';
 import 'mocha';
 const { JSDOM } = require('jsdom');
-const dom = new JSDOM("", {
-    url: `http://localhost:${resolvedPorts.server}`
+const dom = new JSDOM('', {
+    url: `http://localhost:${resolvedPorts.server}`,
 });
 (global as any).window = dom.window;
 
-
-import { autorun, reaction } from "mobx";
+import { reaction } from 'mobx';
+import { resolvedPorts } from '../src/client/util/CurrentUserUtils';
 import { Doc } from '../src/fields/Doc';
+import { createSchema, defaultSpec, makeInterface } from '../src/fields/Schema';
 import { Cast } from '../src/fields/Types';
-import { createSchema, makeInterface, defaultSpec } from '../src/fields/Schema';
 import { ImageField } from '../src/fields/URLField';
-import { resolvedPorts } from '../src/client/util/CurrentUserUtils';
-describe("Document", () => {
+describe('Document', () => {
     it('should hold fields', () => {
-        const key = "Test";
-        const key2 = "Test2";
+        const key = 'Test';
+        const key2 = 'Test2';
         const field = 15;
         const doc = new Doc();
         doc[key] = field;
-        const getField = Cast(doc[key], "number");
-        const getField2 = Cast(doc[key2], "number");
+        const getField = Cast(doc[key], 'number');
+        const getField2 = Cast(doc[key2], 'number');
         expect(getField).to.equal(field);
         expect(getField2).to.equal(undefined);
     });
 
     it('should update', () => {
         const doc = new Doc();
-        const key = "Test";
-        const key2 = "Test2";
+        const key = 'Test';
+        const key2 = 'Test2';
         let ran = false;
-        reaction(() => doc[key], (field) => { ran = true; });
+        reaction(
+            () => doc[key],
+            field => {
+                ran = true;
+            }
+        );
         expect(ran).to.equal(false);
 
         doc[key2] = 4;
@@ -44,55 +48,55 @@ describe("Document", () => {
 });
 
 const testSchema1 = createSchema({
-    a: "number",
-    b: "string",
-    c: "boolean",
+    a: 'number',
+    b: 'string',
+    c: 'boolean',
     d: ImageField,
-    e: Doc
+    e: Doc,
 });
 
 type TestDoc = makeInterface<[typeof testSchema1]>;
 const TestDoc = makeInterface(testSchema1);
 
 const testSchema2 = createSchema({
-    a: defaultSpec("boolean", true),
-    b: defaultSpec("number", 5),
-    c: defaultSpec("string", "hello world")
+    a: defaultSpec('boolean', true),
+    b: defaultSpec('number', 5),
+    c: defaultSpec('string', 'hello world'),
 });
 
 type TestDoc2 = makeInterface<[typeof testSchema2]>;
 const TestDoc2 = makeInterface(testSchema2);
 
 const testSchema3 = createSchema({
-    a: TestDoc2
+    a: TestDoc2,
 });
 
 type TestDoc3 = makeInterface<[typeof testSchema3]>;
 const TestDoc3 = makeInterface(testSchema3);
 
-describe("Schema", () => {
-    it("should do the right thing 1", () => {
-        const test1 = new Doc;
-        const test2 = new Doc;
-        const ifield = new ImageField(new URL("http://google.com"));
+describe('Schema', () => {
+    it('should do the right thing 1', () => {
+        const test1 = new Doc();
+        const test2 = new Doc();
+        const ifield = new ImageField(new URL('http://google.com'));
         test1.a = 5;
-        test1.b = "hello";
+        test1.b = 'hello';
         test1.c = true;
         test1.d = ifield;
         test1.e = test2;
         const doc = TestDoc(test1);
         expect(doc.a).to.equal(5);
-        expect(doc.b).to.equal("hello");
+        expect(doc.b).to.equal('hello');
         expect(doc.c).to.equal(true);
         expect(doc.d).to.equal(ifield);
         expect(doc.e).to.equal(test2);
     });
 
-    it("should do the right thing 2", () => {
-        const test1 = new Doc;
-        const test2 = new Doc;
-        const ifield = new ImageField(new URL("http://google.com"));
-        test1.a = "hello";
+    it('should do the right thing 2', () => {
+        const test1 = new Doc();
+        const test2 = new Doc();
+        const ifield = new ImageField(new URL('http://google.com'));
+        test1.a = 'hello';
         test1.b = 5;
         test1.c = test2;
         test1.d = true;
@@ -105,11 +109,11 @@ describe("Schema", () => {
         expect(doc.e).to.equal(undefined);
     });
 
-    it("should do the right thing 3", () => {
-        const test1 = new Doc;
-        const test2 = new Doc;
-        const ifield = new ImageField(new URL("http://google.com"));
-        test1.a = "hello";
+    it('should do the right thing 3', () => {
+        const test1 = new Doc();
+        const test2 = new Doc();
+        const ifield = new ImageField(new URL('http://google.com'));
+        test1.a = 'hello';
         test1.b = 5;
         test1.c = test2;
         test1.d = true;
@@ -122,40 +126,40 @@ describe("Schema", () => {
         expect(doc.e).to.equal(undefined);
     });
 
-    it("should do the right thing 4", () => {
+    it('should do the right thing 4', () => {
         const doc = TestDoc2();
         expect(doc.a).to.equal(true);
         expect(doc.b).to.equal(5);
-        expect(doc.c).to.equal("hello world");
+        expect(doc.c).to.equal('hello world');
 
-        const d2 = new Doc;
+        const d2 = new Doc();
         d2.a = false;
         d2.b = 4;
-        d2.c = "goodbye";
+        d2.c = 'goodbye';
         const doc2 = TestDoc2(d2);
         expect(doc2.a).to.equal(false);
         expect(doc2.b).to.equal(4);
-        expect(doc2.c).to.equal("goodbye");
+        expect(doc2.c).to.equal('goodbye');
 
-        const d3 = new Doc;
-        d3.a = "hello";
+        const d3 = new Doc();
+        d3.a = 'hello';
         d3.b = false;
         d3.c = 5;
         const doc3 = TestDoc2(d3);
         expect(doc3.a).to.equal(true);
         expect(doc3.b).to.equal(5);
-        expect(doc3.c).to.equal("hello world");
+        expect(doc3.c).to.equal('hello world');
     });
 
-    it("should do the right thing 5", async () => {
-        const test1 = new Doc;
-        const test2 = new Doc;
+    it('should do the right thing 5', async () => {
+        const test1 = new Doc();
+        const test2 = new Doc();
         const doc = TestDoc3(test1);
         expect(doc.a).to.equal(undefined);
         test1.a = test2;
         const doc2 = (await doc.a)!;
         expect(doc2.a).to.equal(true);
         expect(doc2.b).to.equal(5);
-        expect(doc2.c).to.equal("hello world");
+        expect(doc2.c).to.equal('hello world');
     });
 });
-- 
cgit v1.2.3-70-g09d2


From fdc0bf7c54af252178f587709630d36726484b91 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 2 Jan 2024 13:26:53 -0500
Subject: fixing more .props => ._props refernces.

---
 src/client/util/BranchingTrailManager.tsx          |  2 +-
 src/client/util/CaptureManager.tsx                 |  2 +-
 src/client/util/DictationManager.ts                |  8 +--
 src/client/util/DragManager.ts                     |  2 +-
 src/client/util/GroupMemberView.tsx                | 29 +++++----
 src/client/util/SelectionManager.ts                | 10 +--
 src/client/util/SharingManager.tsx                 |  2 +-
 src/client/util/TrackMovements.ts                  |  4 +-
 src/client/views/DocComponent.tsx                  |  3 +-
 src/client/views/DocumentButtonBar.tsx             | 74 +++-------------------
 src/client/views/DocumentDecorations.tsx           | 10 +--
 src/client/views/FilterPanel.tsx                   | 18 ++++--
 src/client/views/InkStrokeProperties.ts            |  2 +-
 src/client/views/PropertiesButtons.tsx             |  6 +-
 src/client/views/StyleProvider.tsx                 |  2 +-
 src/client/views/TemplateMenu.tsx                  |  6 +-
 .../views/collections/CollectionCarousel3DView.tsx |  4 +-
 .../views/collections/CollectionCarouselView.tsx   |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +-
 .../views/collections/CollectionNoteTakingView.tsx |  2 +-
 .../views/collections/CollectionPileView.tsx       |  4 +-
 .../collections/CollectionStackedTimeline.tsx      |  2 +-
 src/client/views/collections/TreeView.tsx          |  6 +-
 .../CollectionFreeFormLinkView.tsx                 |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collectionGrid/CollectionGridView.tsx          | 72 ++++++++++-----------
 .../collectionMulticolumn/MulticolumnResizer.tsx   | 11 +---
 .../collectionMulticolumn/MultirowResizer.tsx      | 11 +---
 .../nodes/DataVizBox/components/LineChart.tsx      |  6 +-
 .../views/nodes/DataVizBox/components/TableBox.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 16 ++---
 src/client/views/nodes/MapBox/MapBox2.tsx          | 34 +++++-----
 src/client/views/nodes/MapBox/MapPushpinBox.tsx    |  4 +-
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   | 54 ++++++++--------
 .../nodes/PhysicsBox/PhysicsSimulationBox.tsx      | 34 +++++-----
 .../views/nodes/RecordingBox/RecordingBox.tsx      |  6 +-
 src/client/views/nodes/ScreenshotBox.tsx           | 50 +++++++--------
 src/client/views/nodes/ScriptingBox.tsx            | 14 ++--
 src/client/views/nodes/audio/AudioWaveform.tsx     |  2 +-
 .../views/nodes/formattedText/DashDocView.tsx      | 16 ++---
 .../views/nodes/formattedText/EquationView.tsx     |  4 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 16 ++---
 .../views/nodes/importBox/ImportElementBox.tsx     |  4 +-
 src/client/views/pdf/Annotation.tsx                | 55 ++++++++--------
 src/client/views/search/SearchBox.tsx              | 10 +--
 src/client/views/selectedDoc/SelectedDocView.tsx   |  6 +-
 47 files changed, 289 insertions(+), 350 deletions(-)

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

diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx
index 11f16493f..02879e3c4 100644
--- a/src/client/util/BranchingTrailManager.tsx
+++ b/src/client/util/BranchingTrailManager.tsx
@@ -54,7 +54,7 @@ export class BranchingTrailManager extends React.Component {
     @observable private docIdToDocMap: Map<String, Doc> = new Map<String, Doc>();
 
     observeDocumentChange = (targetDoc: Doc, pres: PresBox) => {
-        const presId = pres.props.Document[Id];
+        const presId = pres.Document[Id];
         if (this.prevPresId === presId) {
             return;
         }
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index c1e0a5b2e..2e13aff2f 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -84,7 +84,7 @@ export class CaptureManager extends React.Component<{}> {
                         onClick={() => {
                             const selected = SelectionManager.Views.slice();
                             SelectionManager.DeselectAll();
-                            selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
+                            selected.map(dv => dv.props.removeDocument?.(dv.Document));
                             this.close();
                         }}>
                         Cancel
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 039bb360e..82c63695c 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -290,7 +290,7 @@ export namespace DictationManager {
             if (!ctor) {
                 return false;
             }
-            return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined;
+            return Cast(Doc.GetProto(view.Document).data, ctor) !== undefined;
         };
 
         const validate = (target: DocumentView, types: DocumentType[]) => {
@@ -318,7 +318,7 @@ export namespace DictationManager {
             [
                 'clear',
                 {
-                    action: (target: DocumentView) => (Doc.GetProto(target.props.Document).data = new List()),
+                    action: (target: DocumentView) => (Doc.GetProto(target.Document).data = new List()),
                     restrictTo: [DocumentType.COL],
                 },
             ],
@@ -347,7 +347,7 @@ export namespace DictationManager {
                 action: (target: DocumentView, matches: RegExpExecArray) => {
                     const count = interpretNumber(matches[1]);
                     const what = matches[2];
-                    const dataDoc = Doc.GetProto(target.props.Document);
+                    const dataDoc = Doc.GetProto(target.Document);
                     const fieldKey = 'data';
                     if (isNaN(count)) {
                         return;
@@ -372,7 +372,7 @@ export namespace DictationManager {
                 expression: /view as (freeform|stacking|masonry|schema|tree)/g,
                 action: (target: DocumentView, matches: RegExpExecArray) => {
                     const mode = matches[1];
-                    mode && (target.props.Document._type_collection = mode);
+                    mode && (target.Document._type_collection = mode);
                 },
                 restrictTo: [DocumentType.COL],
             }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index fe3a52be7..9ede18ed5 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -142,7 +142,7 @@ export namespace DragManager {
             this.linkSourceGetAnchor = linkSourceGetAnchor;
         }
         get dragDocument() {
-            return this.linkDragView.props.Document;
+            return this.linkDragView.Document;
         }
         linkSourceGetAnchor: () => Doc;
         linkSourceDoc?: Doc;
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx
index 7de0f336f..894583711 100644
--- a/src/client/util/GroupMemberView.tsx
+++ b/src/client/util/GroupMemberView.tsx
@@ -19,35 +19,38 @@ interface GroupMemberViewProps {
 @observer
 export class GroupMemberView extends React.Component<GroupMemberViewProps> {
     @observable private memberSort: 'ascending' | 'descending' | 'none' = 'none';
+    get group() {
+        return this.props.group;
+    }
 
     private get editingInterface() {
-        let members: string[] = this.props.group ? JSON.parse(StrCast(this.props.group.members)) : [];
+        let members: string[] = this.group ? JSON.parse(StrCast(this.group.members)) : [];
         members = this.memberSort === 'ascending' ? members.sort() : this.memberSort === 'descending' ? members.sort().reverse() : members;
 
-        const options: UserOptions[] = this.props.group ? GroupManager.Instance.options.filter(option => !(JSON.parse(StrCast(this.props.group.members)) as string[]).includes(option.value)) : [];
+        const options: UserOptions[] = this.group ? GroupManager.Instance.options.filter(option => !(JSON.parse(StrCast(this.group.members)) as string[]).includes(option.value)) : [];
 
-        const hasEditAccess = GroupManager.Instance.hasEditAccess(this.props.group);
+        const hasEditAccess = GroupManager.Instance.hasEditAccess(this.group);
 
-        return !this.props.group ? null : (
+        return !this.group ? null : (
             <div className="editing-interface" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}>
                 <div className="editing-header">
                     <input
                         className="group-title"
                         style={{ marginLeft: !hasEditAccess ? '-14%' : 0 }}
-                        value={StrCast(this.props.group.title || this.props.group.groupName)}
-                        onChange={e => (this.props.group.title = e.currentTarget.value)}
+                        value={StrCast(this.group.title || this.group.groupName)}
+                        onChange={e => (this.group.title = e.currentTarget.value)}
                         disabled={!hasEditAccess}></input>
                     <div className={'memberView-closeButton'}>
                         <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={action(this.props.onCloseButtonClick)} color={StrCast(Doc.UserDoc().userColor)} />
                     </div>
-                    {GroupManager.Instance.hasEditAccess(this.props.group) ? (
+                    {GroupManager.Instance.hasEditAccess(this.group) ? (
                         <div className="group-buttons">
                             <div style={{ border: StrCast(Doc.UserDoc().userColor) }}>
                                 <Select
                                     className="add-member-dropdown"
                                     isSearchable={true}
                                     options={options}
-                                    onChange={selectedOption => GroupManager.Instance.addMemberToGroup(this.props.group, (selectedOption as UserOptions).value)}
+                                    onChange={selectedOption => GroupManager.Instance.addMemberToGroup(this.group, (selectedOption as UserOptions).value)}
                                     placeholder={'Add members'}
                                     value={null}
                                     styles={{
@@ -72,8 +75,8 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
                                     }}
                                 />
                             </div>
-                            <div className={'delete-button'}>
-                                <Button text={'Delete Group'} type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.deleteGroup(this.props.group)} />
+                            <div className="delete-button">
+                                <Button text="Delete Group" type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.deleteGroup(this.group)} />
                             </div>
                         </div>
                     ) : null}
@@ -81,14 +84,14 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
                         Emails {this.memberSort === 'ascending' ? '↑' : this.memberSort === 'descending' ? '↓' : ''} {/* → */}
                     </div>
                 </div>
-                <div className={'style-divider'} style={{ background: StrCast(Doc.UserDoc().userColor) }} />
+                <div className="style-divider" style={{ background: StrCast(Doc.UserDoc().userColor) }} />
                 <div className="editing-contents" style={{ height: hasEditAccess ? '62%' : '85%' }}>
                     {members.map(member => (
                         <div className="editing-row" key={member}>
                             <div className="user-email">{member}</div>
                             {hasEditAccess ? (
-                                <div className={'remove-button'} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.props.group, member)}>
-                                    <IconButton icon={<FontAwesomeIcon icon={'trash-alt'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.props.group, member)} />
+                                <div className={'remove-button'} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.group, member)}>
+                                    <IconButton icon={<FontAwesomeIcon icon={'trash-alt'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.group, member)} />
                                 </div>
                             ) : null}
                         </div>
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 07bbde36c..53e4a2853 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -33,17 +33,17 @@ export class SelectionManager {
 
     public static SelectView = action((docView: DocumentView | undefined, extendSelection: boolean): void => {
         if (!docView) this.DeselectAll();
-        else if (!docView.SELECTED) {
+        else if (!docView.IsSelected) {
             if (!extendSelection) this.DeselectAll();
             this.Instance.SelectedViews.push(docView);
-            docView.SELECTED = true;
+            docView.IsSelected = true;
             docView._props.whenChildContentsActiveChanged(true);
         }
     });
 
     public static DeselectView = action((docView?: DocumentView): void => {
         if (docView && this.Instance.SelectedViews.includes(docView)) {
-            docView.SELECTED = false;
+            docView.IsSelected = false;
             this.Instance.SelectedViews.splice(this.Instance.SelectedViews.indexOf(docView), 1);
             docView._props.whenChildContentsActiveChanged(false);
         }
@@ -55,14 +55,14 @@ export class SelectionManager {
         LinkManager.currentLinkAnchor = undefined;
         runInAction(() => (this.Instance.SelectedSchemaDocument = undefined));
         this.Instance.SelectedViews.forEach(dv => {
-            dv.SELECTED = false;
+            dv.IsSelected = 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 IsSelected = (doc?: Doc) => Array.from(doc?.[DocViews] ?? []).some(dv => dv?.IsSelected);
     public static get Views() { return this.Instance.SelectedViews; } // prettier-ignore
     public static get SelectedSchemaDoc() { return this.Instance.SelectedSchemaDocument; } // prettier-ignore
     public static get Docs() { return this.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index ac7de9872..fddf735e3 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -94,7 +94,7 @@ export class SharingManager extends React.Component<{}> {
         this.populateUsers();
         runInAction(() => {
             this.targetDocView = target;
-            this.targetDoc = target_doc || target?.props.Document;
+            this.targetDoc = target_doc || target?.Document;
             DictationOverlay.Instance.hasActiveModal = true;
             this.isOpen = this.targetDoc !== undefined;
             this.permissions = SharingPermissions.Augment;
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index 0b197cf9a..f9c2d522f 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -126,12 +126,12 @@ export class TrackMovements {
         }
 
         // init the dispose funcs on the page
-        const docList = DocListCast(CollectionDockingView.Instance?.props.Document.data);
+        const docList = DocListCast(CollectionDockingView.Instance?.Document.data);
         this.updateRecordingFFViewsFromTabs(docList);
 
         // create a reaction to monitor changes in tabs
         this.tabChangeDisposeFunc = reaction(
-            () => CollectionDockingView.Instance?.props.Document.data,
+            () => CollectionDockingView.Instance?.Document.data,
             change => {
                 // TODO: consider changing between dashboards
                 // console.info('change in tabs', change);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 235b0dc68..b5bd17572 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -55,7 +55,6 @@ interface ViewBoxBaseProps {
 export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
     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;
         }
@@ -127,7 +126,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             const indocs = doc instanceof Doc ? [doc] : doc;
             const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin);
 
-            // docs.forEach(doc => doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true));
+            // docs.forEach(doc => doc.annotationOn === this.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true));
             const targetDataDoc = this.dataDoc;
             const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]);
             const toRemove = value.filter(v => docs.includes(v));
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 3938b81cd..63d2a5e08 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -25,27 +25,10 @@ import { DashFieldView } from './nodes/formattedText/DashFieldView';
 import { PinProps } from './nodes/trails';
 import { faCalendarDays } from '@fortawesome/free-solid-svg-icons';
 
-const cloud: IconProp = 'cloud-upload-alt';
-const fetch: IconProp = 'sync-alt';
-
-enum UtilityButtonState {
-    Default,
-    OpenRight,
-    OpenExternally,
-}
-
 @observer
 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 public isAnimatingFetch = false;
-    @observable public isAnimatingPulse = false;
-
     @observable public static Instance: DocumentButtonBar;
-    public static hasPushedHack = false;
-    public static hasPulledHack = false;
 
     constructor(props: any) {
         super(props);
@@ -53,51 +36,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         DocumentButtonBar.Instance = this;
     }
 
-    public startPullOutcome = action((success: boolean) => {
-        if (!this._pullAnimating) {
-            this._pullAnimating = true;
-            this.pullIcon = success ? 'check-circle' : 'stop-circle';
-            setTimeout(
-                () =>
-                    runInAction(() => {
-                        this.pullIcon = 'arrow-alt-circle-down';
-                        this._pullAnimating = false;
-                    }),
-                1000
-            );
-        }
-    });
-
-    public startPushOutcome = action((success: boolean) => {
-        this.isAnimatingPulse = false;
-        if (!this._pushAnimating) {
-            this._pushAnimating = true;
-            this.pushIcon = success ? 'check-circle' : 'stop-circle';
-            setTimeout(
-                () =>
-                    runInAction(() => {
-                        this.pushIcon = 'arrow-alt-circle-up';
-                        this._pushAnimating = false;
-                    }),
-                1000
-            );
-        }
-    });
-
-    public setPullState = action((unchanged: boolean) => {
-        this.isAnimatingFetch = false;
-        if (!this._pullColorAnimating) {
-            this._pullColorAnimating = true;
-            this.pullColor = unchanged ? 'lawngreen' : 'red';
-            setTimeout(this.clearPullColor, 1000);
-        }
-    });
-
-    private clearPullColor = action(() => {
-        this.pullColor = 'white';
-        this._pullColorAnimating = false;
-    });
-
     get view0() {
         return this._props.views()?.[0];
     }
@@ -367,7 +305,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         if (this._dragRef.current) {
             const dragDocView = this.view0!;
             const dragData = new DragManager.DocumentDragData([dragDocView.Document]);
-            const [left, top] = dragDocView._props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+            const [left, top] = dragDocView.screenToLocalTransform().inverse().transformPoint(0, 0);
             dragData.defaultDropAction = 'embed';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false });
@@ -385,7 +323,8 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         return !view0 ? null : (
             <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embedding</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
                 <div className="documentButtonBar-linkFlyout" ref={this._dragRef} onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}>
-                    {/* <Flyout
+                    {
+                        /* <Flyout
                         anchorPoint={anchorPoints.LEFT_TOP}
                         onOpen={action(() => (this._embedDown = true))}
                         onClose={action(() => (this._embedDown = false))}
@@ -400,7 +339,12 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
                         <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onTemplateButton}>
                             <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />
                         </div>
-                    </Flyout> */}
+                    </Flyout> */
+
+                        <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onTemplateButton}>
+                            <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />
+                        </div>
+                    }
                 </div>
             </Tooltip>
         );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index dab6e6193..51fd33222 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -208,7 +208,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             SelectionManager.Views.map(dv => dv.Document),
             dragDocView._props.dropAction
         );
-        dragData.offset = dragDocView._props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top);
+        dragData.offset = dragDocView.screenToLocalTransform().transformDirection(e.x - left, e.y - top);
         dragData.moveDocument = dragDocView._props.moveDocument;
         dragData.removeDocument = dragDocView._props.removeDocument;
         dragData.isDocDecorationMove = true;
@@ -353,7 +353,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     };
 
     setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => {
-        const newloccentern = seldocview._props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]);
+        const newloccentern = seldocview.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.Document._rotation) / 180) * Math.PI);
         seldocview.Document.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width);
@@ -462,7 +462,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
             return [a[0] + atob[0] * t, a[1] + atob[1] * t];
         };
-        const tl = docView._props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+        const tl = docView.screenToLocalTransform().inverse().transformPoint(0, 0);
         return project([e.clientX + this._offset.x, e.clientY + this._offset.y], tl, [tl[0] + fixedAspect, tl[1] + 1]);
     };
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
@@ -530,7 +530,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             doc.xPadding = NumCast(doc.xPadding) * scale.x;
             doc.yPadding = NumCast(doc.yPadding) * scale.y;
         } else {
-            const refCent = docView._props.ScreenToLocalTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move)
+            const refCent = docView.screenToLocalTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move)
             const [nwidth, nheight] = [docView.nativeWidth, docView.nativeHeight];
             const [initWidth, initHeight] = [NumCast(doc._width, 1), NumCast(doc._height)];
 
@@ -632,7 +632,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     @computed get rotCenter() {
         const lastView = SelectionManager.Views.lastElement();
         if (lastView) {
-            const invXf = lastView._props.ScreenToLocalTransform().inverse();
+            const invXf = lastView.screenToLocalTransform().inverse();
             const seldoc = lastView.layoutDoc;
             const loccenter = Utils.rotPt(NumCast(seldoc.rotation_centerX) * NumCast(seldoc._width), NumCast(seldoc.rotation_centerY) * NumCast(seldoc._height), invXf.Rotate);
             return invXf.transformPoint(loccenter.x + NumCast(seldoc._width) / 2, loccenter.y + NumCast(seldoc._height) / 2);
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index d3075a5cd..c4f65a5ca 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, ObservableMap } from 'mobx';
+import { action, computed, makeObservable, observable, ObservableMap } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
@@ -18,22 +18,28 @@ import { undoable } from '../util/UndoManager';
 import './FilterPanel.scss';
 import { FieldView } from './nodes/FieldView';
 import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components';
+import { ObservableReactComponent } from './ObservableReactComponent';
 
 interface filterProps {
     Document: Doc;
 }
 
 @observer
-export class FilterPanel extends React.Component<filterProps> {
+export class FilterPanel extends ObservableReactComponent<filterProps> {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(FilterPanel, fieldKey);
     }
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
+
     /**
      * @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.Document;
+    get targetDoc() {
+        return this._props.Document;
     }
     @computed get targetDocChildKey() {
         const targetView = DocumentManager.Instance.getFirstDocumentView(this.targetDoc);
@@ -112,7 +118,7 @@ export class FilterPanel extends React.Component<filterProps> {
     // }
 
     gatherFieldValues(childDocs: Doc[], facetKey: string) {
-        const valueSet = new Set<string>(StrListCast(this.props.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1]));
+        const valueSet = new Set<string>(StrListCast(this.targetDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]));
         let rtFields = 0;
         let subDocs = childDocs;
         if (subDocs.length > 0) {
@@ -223,7 +229,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.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]);
+        const set = new Set<string>([...StrListCast(this.targetDoc.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/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index a628c7120..f31ea2994 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -244,7 +244,7 @@ export class InkStrokeProperties {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const order = controlIndex % 4;
             const closed = InkingStroke.IsClosed(ink);
-            const brokenIndices = Cast(inkView.props.Document.brokenInkIndices, listSpec('number'), []);
+            const brokenIndices = Cast(inkView.Document.brokenInkIndices, listSpec('number'), []);
             if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) {
                 const cpt_before = ink[controlIndex];
                 const cpt = { X: cpt_before.X + deltaX, Y: cpt_before.Y + deltaY };
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index f1dadcd5e..5905f8dbc 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -411,7 +411,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         SelectionManager.Views.filter(dv => dv.docView)
             .map(dv => dv.docView!)
             .forEach(docView => {
-                const linkButton = IsFollowLinkScript(docView.props.Document.onClick);
+                const linkButton = IsFollowLinkScript(docView.Document.onClick);
                 docView.noOnClick();
                 switch (onClick) {
                     case 'enterPortal':
@@ -422,11 +422,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                         break;
                     case 'linkInPlace':
                         docView.toggleFollowLink(false, false);
-                        docView.props.Document.followLinkLocation = linkButton ? OpenWhere.lightbox : undefined;
+                        docView.Document.followLinkLocation = linkButton ? OpenWhere.lightbox : undefined;
                         break;
                     case 'linkOnRight':
                         docView.toggleFollowLink(false, false);
-                        docView.props.Document.followLinkLocation = linkButton ? OpenWhere.addRight : undefined;
+                        docView.Document.followLinkLocation = linkButton ? OpenWhere.addRight : undefined;
                         break;
                 }
             });
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index c8f6d6013..835e61d55 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -121,7 +121,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.Highlighting:
             if (doc && (Doc.IsSystem(doc) || doc.type === DocumentType.FONTICON)) return undefined;            
             if (doc && !doc.layout_disableBrushing && !props?.disableBrushing) {
-                const selected = Array.from(doc?.[DocViews]??[]).filter(dv => dv.SELECTED).length;
+                const selected = Array.from(doc?.[DocViews]??[]).filter(dv => dv.IsSelected).length;
                 const highlightIndex = Doc.GetBrushHighlightStatus(doc) || (selected ? Doc.DocBrushStatus.selfBrushed : 0);
                 const highlightColor = ['transparent', 'rgb(68, 118, 247)', selected ? "black" : 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
                 const highlightStyle = ['solid', 'dashed', 'solid', 'solid', 'solid'][highlightIndex];
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 791928b4a..b42f3c1e9 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -79,14 +79,14 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     };
     componentDidMount() {
         !this._addedKeys && (this._addedKeys = new ObservableSet());
-        [...Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document))), ...Array.from(Object.keys(this.props.docViews[0].props.Document))]
+        [...Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].Document))), ...Array.from(Object.keys(this.props.docViews[0].Document))]
             .filter(key => key.startsWith('layout_'))
             .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', ''))));
     }
 
     return100 = () => 300;
     @computed get scriptField() {
-        const script = ScriptField.MakeScript('docs.map(d => switchView(d, this))', { this: Doc.name }, { docs: this.props.docViews.map(dv => dv.props.Document) as any });
+        const script = ScriptField.MakeScript('docs.map(d => switchView(d, this))', { this: Doc.name }, { docs: this.props.docViews.map(dv => dv.Document) as any });
         return script ? () => script : undefined;
     }
     templateIsUsed = (selDoc: Doc, templateDoc: Doc) => {
@@ -95,7 +95,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     };
     render() {
         TraceMobx();
-        const firstDoc = this.props.docViews[0].props.Document;
+        const firstDoc = this.props.docViews[0].Document;
         const templateName = StrCast(firstDoc.layout_fieldKey, 'layout').replace('layout_', '');
         const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
         const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index a11c53d4d..bf308904e 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -143,10 +143,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         const whichButton = this.layoutDoc.showScrollButton;
         return (
             <>
-                <div className={`carousel3DView-back-scroll${whichButton === 'back' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(-1)}>
+                <div className={`carousel3DView-back-scroll${whichButton === 'back' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(-1)}>
                     {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={'pause'} size={'1x'} /> : <FontAwesomeIcon icon={'angle-double-left'} size={'1x'} />}
                 </div>
-                <div className={`carousel3DView-fwd-scroll${whichButton === 'fwd' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(1)}>
+                <div className={`carousel3DView-fwd-scroll${whichButton === 'fwd' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(1)}>
                     {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={'pause'} size={'1x'} /> : <FontAwesomeIcon icon={'angle-double-right'} size={'1x'} />}
                 </div>
             </>
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 5d09f14ef..b6acf3153 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -120,7 +120,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                     color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
                 }}>
                 {this.content}
-                {this.props.Document._chromeHidden ? null : this.buttons}
+                {this.Document._chromeHidden ? null : this.buttons}
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 868df7a3b..c46f54c70 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -493,7 +493,7 @@ export class CollectionDockingView extends CollectionSubView() {
             Doc.RemoveDocFromList(Doc.GetProto(tab.DashDoc), 'proto_embeddings', tab.DashDoc);
         }
         if (CollectionDockingView.Instance) {
-            const dview = CollectionDockingView.Instance.props.Document;
+            const dview = CollectionDockingView.Instance.Document;
             const fieldKey = CollectionDockingView.Instance.props.fieldKey;
             Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
             this.tabMap.delete(tab);
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 2a6cb081f..98ae01591 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -446,7 +446,7 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
     @observable private suggestions: string[] = [];
 
     get document() {
-        return this.props.docView.props.Document;
+        return this.props.docView.Document;
     }
 
     @computed private get descending() {
@@ -572,7 +572,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     @observable private resize = false;
     private resizeListenerDisposer: Opt<Lambda>;
     get document() {
-        return this.props.docView.props.Document;
+        return this.props.docView.Document;
     }
 
     @computed get panelWidth() {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index f9d258490..0744814dd 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -527,7 +527,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
                 colHeaderData={this.colHeaderData}
-                Document={this.props.Document}
+                Document={this.Document}
                 TemplateDataDocument={this.props.TemplateDataDocument}
                 resizeColumns={this.resizeColumns}
                 renderChildren={this.children}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index e5068645f..359fdf543 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -94,7 +94,7 @@ export class CollectionPileView extends CollectionSubView() {
             DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
             this.layoutDoc._freeform_panX = 0;
             this.layoutDoc._freeform_panY = -10;
-            this.props.Document._freeform_pileEngine = computePassLayout.name;
+            this.Document._freeform_pileEngine = computePassLayout.name;
         } else {
             const defaultSize = NumCast(this.Document._starburstDiameter, 400);
             this.Document.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width) / 2 - defaultSize / 2;
@@ -104,7 +104,7 @@ export class CollectionPileView extends CollectionSubView() {
             this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
             this.layoutDoc._width = this.layoutDoc._height = defaultSize;
             this.layoutDoc.background;
-            this.props.Document._freeform_pileEngine = computeStarburstLayout.name;
+            this.Document._freeform_pileEngine = computeStarburstLayout.name;
         }
     });
 
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index fa3844aa4..a63688354 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -843,7 +843,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
         return (
             <div style={{ pointerEvents: this.noEvents ? 'none' : undefined }}>
                 {inner.view}
-                {!inner.anchor.view || !inner.anchor.view.SELECTED ? null : (
+                {!inner.anchor.view || !inner.anchor.view.IsSelected ? null : (
                     <>
                         <div key="left" className="collectionStackedTimeline-left-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this._props.mark, true)} />
                         <div key="right" className="collectionStackedTimeline-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this._props.mark, false)} />
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 72882ac17..f2ceaa681 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -169,7 +169,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         return this.childDocList(this.fieldKey + '_annotations');
     }
     @computed get selected() {
-        return this._docRef?.SELECTED;
+        return this._docRef?.IsSelected;
     }
 
     childDocList(field: string) {
@@ -204,10 +204,10 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         this._disposers.selection?.();
         if (!docView) {
             this._editTitle = false;
-        } else if (docView.SELECTED) {
+        } else if (docView.IsSelected) {
             this._editTitle = true;
             this._disposers.selection = reaction(
-                () => docView.SELECTED,
+                () => docView.IsSelected,
                 isSel => !isSel && this.setEditTitle(undefined)
             );
         } else {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 6337c8d34..6131fe037 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -229,8 +229,8 @@ export class CollectionFreeFormLinkView extends ObservableReactComponent<Collect
         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.SELECTED || A.Document[Brushed];
-        const bActive = B.SELECTED || B.Document[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 cc0833698..44654c8fe 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -912,7 +912,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 for (var j = 0; j < otherCtrlPts.length - 3; j += 4) {
                     const neighboringSegment = i === j || i === j - 4 || i === j + 4;
                     // Ensuring that the curve intersected by the eraser is not checked for further ink intersections.
-                    if (ink?.Document === otherInk.props.Document && neighboringSegment) continue;
+                    if (ink?.Document === otherInk.Document && neighboringSegment) continue;
 
                     const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y })));
                     const c0 = otherCurve.get(0);
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 3e75257e5..0f750c4f8 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -33,35 +33,35 @@ export class CollectionGridView extends CollectionSubView() {
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
 
     @computed get numCols() {
-        return NumCast(this.props.Document.gridNumCols, 10);
+        return NumCast(this.Document.gridNumCols, 10);
     }
     @computed get rowHeight() {
-        return this._rowHeight === undefined ? NumCast(this.props.Document.gridRowHeight, 100) : this._rowHeight;
+        return this._rowHeight === undefined ? NumCast(this.Document.gridRowHeight, 100) : this._rowHeight;
     }
     // sets the default width and height of the grid nodes
     @computed get defaultW() {
-        return NumCast(this.props.Document.gridDefaultW, 2);
+        return NumCast(this.Document.gridDefaultW, 2);
     }
     @computed get defaultH() {
-        return NumCast(this.props.Document.gridDefaultH, 2);
+        return NumCast(this.Document.gridDefaultH, 2);
     }
 
     @computed get colWidthPlusGap() {
-        return (this.props.PanelWidth() - this.margin) / this.numCols;
+        return (this._props.PanelWidth() - this.margin) / this.numCols;
     }
     @computed get rowHeightPlusGap() {
         return this.rowHeight + this.margin;
     }
 
     @computed get margin() {
-        return NumCast(this.props.Document.margin, 10);
+        return NumCast(this.Document.margin, 10);
     } // sets the margin between grid nodes
 
     @computed get flexGrid() {
-        return BoolCast(this.props.Document.gridFlex, true);
+        return BoolCast(this.Document.gridFlex, true);
     } // is grid static/flexible i.e. whether nodes be moved around and resized
     @computed get compaction() {
-        return StrCast(this.props.Document.gridStartCompaction, StrCast(this.props.Document.gridCompaction, 'vertical'));
+        return StrCast(this.Document.gridStartCompaction, StrCast(this.Document.gridCompaction, 'vertical'));
     } // is grid static/flexible i.e. whether nodes be moved around and resized
 
     /**
@@ -94,12 +94,12 @@ export class CollectionGridView extends CollectionSubView() {
 
         // updates the layouts if the reset button has been clicked
         this._resetListenerDisposer = reaction(
-            () => this.props.Document.gridResetLayout,
+            () => this.Document.gridResetLayout,
             reset => {
                 if (reset && this.flexGrid) {
                     this.setLayout(this.childLayoutPairs.map((pair, index) => this.makeLayoutItem(pair.layout, this.unflexedPosition(index))));
                 }
-                this.props.Document.gridResetLayout = false;
+                this.Document.gridResetLayout = false;
             }
         );
     }
@@ -130,7 +130,7 @@ export class CollectionGridView extends CollectionSubView() {
      * Maps the x- and y- coordinates of the event to a grid cell.
      */
     screenToCell(sx: number, sy: number) {
-        const pt = this.props.ScreenToLocalTransform().transformPoint(sx, sy);
+        const pt = this._props.ScreenToLocalTransform().transformPoint(sx, sy);
         const x = Math.floor(pt[0] / this.colWidthPlusGap);
         const y = Math.floor((pt[1] + this._scroll) / this.rowHeight);
         return { x, y };
@@ -159,25 +159,25 @@ export class CollectionGridView extends CollectionSubView() {
         const xypos = this.flexGrid ? layout : this.unflexedPosition(this.renderedLayoutList.findIndex(l => l.i === layout.i));
         const pos = { x: xypos.x * this.colWidthPlusGap + this.margin, y: xypos.y * this.rowHeightPlusGap + this.margin - this._scroll };
 
-        return this.props.ScreenToLocalTransform().translate(-pos.x, -pos.y);
+        return this._props.ScreenToLocalTransform().translate(-pos.x, -pos.y);
     };
 
     /**
      * @returns the layout list converted from JSON
      */
     get savedLayoutList() {
-        return (this.props.Document.gridLayoutString ? JSON.parse(StrCast(this.props.Document.gridLayoutString)) : []) as Layout[];
+        return (this.Document.gridLayoutString ? JSON.parse(StrCast(this.Document.gridLayoutString)) : []) as Layout[];
     }
 
     /**
      * Stores the layout list on the Document as JSON
      */
     setLayoutList(layouts: Layout[]) {
-        this.props.Document.gridLayoutString = JSON.stringify(layouts);
+        this.Document.gridLayoutString = JSON.stringify(layouts);
     }
 
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
+    isContentActive = () => this._props.isSelected() || this._props.isContentActive();
+    isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
     /**
      *
      * @param layout
@@ -199,9 +199,9 @@ export class CollectionGridView extends CollectionSubView() {
                 PanelWidth={width}
                 PanelHeight={height}
                 ScreenToLocalTransform={dxf}
-                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
                 onClick={this.onChildClickHandler}
-                renderDepth={this.props.renderDepth + 1}
+                renderDepth={this._props.renderDepth + 1}
                 dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
             />
         );
@@ -222,12 +222,12 @@ export class CollectionGridView extends CollectionSubView() {
                 if (gridLayout) Object.assign(gridLayout, layoutArray.find(layout => layout.i === doc[Id]) || gridLayout);
             });
 
-            if (this.props.Document.gridStartCompaction) {
+            if (this.Document.gridStartCompaction) {
                 undoBatch(() => {
-                    this.props.Document.gridCompaction = this.props.Document.gridStartCompaction;
+                    this.Document.gridCompaction = this.Document.gridStartCompaction;
                     this.setLayoutList(savedLayouts);
                 })();
-                this.props.Document.gridStartCompaction = undefined;
+                this.Document.gridStartCompaction = undefined;
             } else {
                 undoBatch(() => this.setLayoutList(savedLayouts))();
             }
@@ -249,7 +249,7 @@ export class CollectionGridView extends CollectionSubView() {
                 const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
                 child &&
                     collector.push(
-                        <div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this.props.isSelected() ? '' : ' static')}>
+                        <div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this._props.isSelected() ? '' : ' static')}>
                             {this.getDisplayDoc(child.layout, dxf, width, height)}
                         </div>
                     );
@@ -317,7 +317,7 @@ export class CollectionGridView extends CollectionSubView() {
             e,
             returnFalse,
             action(() => {
-                undoBatch(() => (this.props.Document.gridRowHeight = this._rowHeight))();
+                undoBatch(() => (this.Document.gridRowHeight = this._rowHeight))();
                 this._rowHeight = undefined;
             }),
             emptyFunction,
@@ -331,8 +331,8 @@ export class CollectionGridView extends CollectionSubView() {
      */
     onContextMenu = () => {
         const displayOptionsMenu: ContextMenuProps[] = [];
-        displayOptionsMenu.push({ description: 'Toggle Content Display Style', event: () => (this.props.Document.display = this.props.Document.display ? undefined : 'contents'), icon: 'copy' });
-        displayOptionsMenu.push({ description: 'Toggle Vertical Centering', event: () => (this.props.Document.centerY = !this.props.Document.centerY), icon: 'copy' });
+        displayOptionsMenu.push({ description: 'Toggle Content Display Style', event: () => (this.Document.display = this.Document.display ? undefined : 'contents'), icon: 'copy' });
+        displayOptionsMenu.push({ description: 'Toggle Vertical Centering', event: () => (this.Document.centerY = !this.Document.centerY), icon: 'copy' });
         ContextMenu.Instance.addItem({ description: 'Display', subitems: displayOptionsMenu, icon: 'tv' });
     };
 
@@ -340,7 +340,7 @@ export class CollectionGridView extends CollectionSubView() {
      * Handles text document creation on double click.
      */
     onPointerDown = (e: React.PointerEvent) => {
-        if (this.props.isContentActive(true)) {
+        if (this._props.isContentActive(true)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -352,7 +352,7 @@ export class CollectionGridView extends CollectionSubView() {
                             action(() => {
                                 const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 });
                                 FormattedTextBox.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
-                                Doc.AddDocToList(this.props.Document, this.props.fieldKey, text);
+                                Doc.AddDocToList(this.Document, this._props.fieldKey, text);
                                 this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
                             })
                         )();
@@ -360,7 +360,7 @@ export class CollectionGridView extends CollectionSubView() {
                 },
                 false
             );
-            if (this.props.isSelected()) e.stopPropagation();
+            if (this._props.isSelected()) e.stopPropagation();
         }
     };
 
@@ -369,7 +369,7 @@ export class CollectionGridView extends CollectionSubView() {
             <div
                 className="collectionGridView-contents"
                 ref={this.createDashEventsTarget}
-                style={{ pointerEvents: !this.props.isContentActive() ? 'none' : undefined }}
+                style={{ pointerEvents: !this._props.isContentActive() ? 'none' : undefined }}
                 onContextMenu={this.onContextMenu}
                 onPointerDown={this.onPointerDown}
                 onDrop={this.onExternalDrop}>
@@ -379,29 +379,29 @@ export class CollectionGridView extends CollectionSubView() {
                     style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white') }}
                     onWheel={e => e.stopPropagation()}
                     onScroll={action(e => {
-                        if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
+                        if (!this._props.isSelected()) e.currentTarget.scrollTop = this._scroll;
                         else this._scroll = e.currentTarget.scrollTop;
                     })}>
                     <Grid
-                        width={this.props.PanelWidth()}
+                        width={this._props.PanelWidth()}
                         nodeList={this.contents.length ? this.contents : null}
                         layout={this.contents.length ? this.renderedLayoutList : undefined}
-                        childrenDraggable={this.props.isSelected() ? true : false}
+                        childrenDraggable={this._props.isSelected() ? true : false}
                         numCols={this.numCols}
                         rowHeight={this.rowHeight}
                         setLayout={this.setLayout}
-                        transformScale={this.props.ScreenToLocalTransform().Scale}
+                        transformScale={this._props.ScreenToLocalTransform().Scale}
                         compactType={this.compaction} // determines whether nodes should remain in position, be bound to the top, or to the left
-                        preventCollision={BoolCast(this.props.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
+                        preventCollision={BoolCast(this.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
                         margin={this.margin}
                     />
                     <input
                         className="rowHeightSlider"
                         type="range"
-                        style={{ width: this.props.PanelHeight() - 30 }}
+                        style={{ width: this._props.PanelHeight() - 30 }}
                         min={1}
                         value={this.rowHeight}
-                        max={this.props.PanelHeight() - 30}
+                        max={this._props.PanelHeight() - 30}
                         onPointerDown={this.onSliderDown}
                         onChange={this.onSliderChange}
                     />
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index ea99bff2e..c38c6dc4e 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -18,12 +18,8 @@ interface ResizerProps {
     select: (isCtrlPressed: boolean) => void;
 }
 
-const resizerOpacity = 1;
-
 @observer
 export default class ResizeBar extends React.Component<ResizerProps> {
-    @observable private isHoverActive = false;
-    @observable private isResizingActive = false;
     private _resizeUndo?: UndoManager.Batch;
 
     @action
@@ -35,7 +31,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
         window.removeEventListener('pointerup', this.onPointerUp);
         window.addEventListener('pointermove', this.onPointerMove);
         window.addEventListener('pointerup', this.onPointerUp);
-        this.isResizingActive = true;
         this._resizeUndo = UndoManager.StartBatch('multcol resizing');
     };
 
@@ -80,8 +75,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
 
     @action
     private onPointerUp = () => {
-        this.isResizingActive = false;
-        this.isHoverActive = false;
         window.removeEventListener('pointermove', this.onPointerMove);
         window.removeEventListener('pointerup', this.onPointerUp);
         this._resizeUndo?.end();
@@ -96,9 +89,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
                     pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
                     width: this.props.width,
                     backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
-                }}
-                onPointerEnter={action(() => (this.isHoverActive = true))}
-                onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
+                }}>
                 <div className={'multiColumnResizer-hdl'} onPointerDown={e => this.registerResizing(e)} />
             </div>
         );
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 7dee65e58..6f1b3b425 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -17,12 +17,8 @@ interface ResizerProps {
     toBottom?: Doc;
 }
 
-const resizerOpacity = 1;
-
 @observer
 export default class ResizeBar extends React.Component<ResizerProps> {
-    @observable private isHoverActive = false;
-    @observable private isResizingActive = false;
     private _resizeUndo?: UndoManager.Batch;
 
     @action
@@ -33,7 +29,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
         window.removeEventListener('pointerup', this.onPointerUp);
         window.addEventListener('pointermove', this.onPointerMove);
         window.addEventListener('pointerup', this.onPointerUp);
-        this.isResizingActive = true;
         this._resizeUndo = UndoManager.StartBatch('multcol resizing');
     };
 
@@ -78,8 +73,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
 
     @action
     private onPointerUp = () => {
-        this.isResizingActive = false;
-        this.isHoverActive = false;
         window.removeEventListener('pointermove', this.onPointerMove);
         window.removeEventListener('pointerup', this.onPointerUp);
         this._resizeUndo?.end();
@@ -94,9 +87,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
                     pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
                     height: this.props.height,
                     backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
-                }}
-                onPointerEnter={action(() => (this.isHoverActive = true))}
-                onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
+                }}>
                 <div className={'multiRowResizer-hdl'} onPointerDown={e => this.registerResizing(e)} />
             </div>
         );
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index a69f083d1..50a8bf83d 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -358,7 +358,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
         const selectedPt = this._currSelected ? `{ ${this._props.axes[0]}: ${this._currSelected.x} ${this._props.axes[1]}: ${this._currSelected.y} }` : 'none';
         if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length == 0) {
             return this._props.axes.length >= 2 && /\d/.test(this._props.records[0][this._props.axes[0]]) && /\d/.test(this._props.records[0][this._props.axes[1]]) ? (
-                <div className="chart-container" style={{ width: this.props.width + this.props.margin.right }}>
+                <div className="chart-container" style={{ width: this._props.width + this._props.margin.right }}>
                     <div className="graph-title">
                         <EditableText
                             val={StrCast(this._props.layoutDoc[titleAccessor])}
@@ -378,8 +378,8 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
                             <Button
                                 onClick={e => {
                                     console.log('test plzz');
-                                    this.props.vizBox.sidebarBtnDown;
-                                    this.props.vizBox.sidebarAddDocument;
+                                    this._props.vizBox.sidebarBtnDown;
+                                    this._props.vizBox.sidebarAddDocument;
                                 }}></Button>
                         </div>
                     ) : null}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index f127fecf3..5365fe1b2 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -169,7 +169,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
             return (
                 <div
                     className="tableBox"
-                    style={{ width: this.props.width + this.props.margin.right }}
+                    style={{ width: this._props.width + this._props.margin.right }}
                     tabIndex={0}
                     onKeyDown={e => {
                         if (this._props.layoutDoc && e.key === 'a' && (e.ctrlKey || e.metaKey)) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f8f4b94a2..d07824099 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -881,7 +881,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     rootSelected = () => this._rootSelected;
     panelHeight = () => this._props.PanelHeight() - this.headerMargin;
-    screenToLocal = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin);
+    contentScreenToLocal = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
     setHeight = (height: number) => !this._props.suppressSetHeight && (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
@@ -925,7 +925,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     PanelHeight={this.panelHeight}
                     setHeight={this.setHeight}
                     isContentActive={this.isContentActive}
-                    ScreenToLocalTransform={this.screenToLocal}
+                    ScreenToLocalTransform={this.contentScreenToLocal}
                     rootSelected={this.rootSelected}
                     onClick={this.onClickFunc}
                     setTitleFocus={this.setTitleFocus}
@@ -1329,10 +1329,10 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
     }
 
     @observable _selected = false;
-    public get SELECTED() {
+    public get IsSelected() {
         return this._selected;
     }
-    public set SELECTED(val) {
+    public set IsSelected(val) {
         runInAction(() => (this._selected = val));
     }
     @observable public static LongPress = false;
@@ -1431,9 +1431,9 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
         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.SELECTED ? ':selected' : ''));
+        return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HideLinkBtn + (this.IsSelected ? ':selected' : ''));
     }
-    hideLinkCount = () => this._props.renderDepth === -1 || (this.SELECTED && this._props.renderDepth) || !this._isHovering || this.hideLinkButton;
+    hideLinkCount = () => this._props.renderDepth === -1 || (this.IsSelected && this._props.renderDepth) || !this._isHovering || this.hideLinkButton;
     @computed get linkCountView() {
         return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
     }
@@ -1568,9 +1568,9 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
     layout_fitWidthFunc = (doc: Doc) => BoolCast(this.layout_fitWidth);
     scaleToScreenSpace = () => (1 / (this._props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
     docViewPathFunc = () => this.docViewPath;
-    isSelected = () => this.SELECTED;
+    isSelected = () => this.IsSelected;
     select = (extendSelection: boolean, focusSelection?: boolean) => {
-        if (this.SELECTED && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this);
+        if (this.IsSelected && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this);
         else {
             SelectionManager.SelectView(this, extendSelection);
             if (focusSelection) {
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 1dbbbb633..722a347f1 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -127,7 +127,7 @@
 //     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
 
 //     componentDidMount() {
-//         this.props.setContentView?.(this);
+//         this._props.setContentView?.(this);
 //     }
 
 //     @action
@@ -358,9 +358,9 @@
 //             e,
 //             (e, down, delta) =>
 //                 runInAction(() => {
-//                     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 fullWidth = NumCast(this.layoutDoc._width);
 //                     const mapWidth = fullWidth - this.sidebarWidth();
@@ -380,12 +380,12 @@
 //         );
 //     };
 
-//     sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+//     sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth();
 //     @computed get layout_sidebarWidthPercent() {
 //         return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
 //     }
 //     @computed get sidebarColor() {
-//         return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+//         return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this._props.fieldKey + '_backgroundColor'], '#e4e4e4'));
 //     }
 
 //     /**
@@ -444,7 +444,7 @@
 //                 key="sidebar"
 //                 title="Toggle Sidebar"
 //                 style={{
-//                     display: !this.props.isContentActive() ? 'none' : undefined,
+//                     display: !this._props.isContentActive() ? 'none' : undefined,
 //                     top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
 //                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
 //                 }}
@@ -481,7 +481,7 @@
 //     };
 
 //     pointerEvents = () => {
-//         return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.IsDragging ? undefined : 'none';
+//         return this._props.isContentActive() === false ? 'none' : this._props.isContentActive() && this._props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.IsDragging ? undefined : 'none';
 //     };
 //     @computed get annotationLayer() {
 //         return (
@@ -489,7 +489,7 @@
 //                 {this.inlineTextAnnotations
 //                     .sort((a, b) => NumCast(a.y) - NumCast(b.y))
 //                     .map(anno => (
-//                         <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
+//                         <Annotation key={`${anno[Id]}-annotation`} {...this._props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
 //                     ))}
 //             </div>
 //         );
@@ -515,13 +515,13 @@
 //         // }
 //     };
 
-//     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
-//     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-//     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
-//     transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
-//     opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
-//     infoWidth = () => this.props.PanelWidth() / 5;
-//     infoHeight = () => this.props.PanelHeight() / 5;
+//     panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1) - this.sidebarWidth();
+//     panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
+//     scrollXf = () => this._props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
+//     transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter];
+//     opaqueFilter = () => [...this._props.childFilters(), Utils.OpaqueBackgroundFilter];
+//     infoWidth = () => this._props.PanelWidth() / 5;
+//     infoHeight = () => this._props.PanelHeight() / 5;
 //     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
 //     savedAnnotations = () => this._savedAnnotations;
 
@@ -556,7 +556,7 @@
 //                                 .map(marker => (
 //                                     <MapBoxInfoWindow
 //                                         key={marker[Id]}
-//                                         {...this.props}
+//                                         {...this._props}
 //                                         setContentView={emptyFunction}
 //                                         place={marker}
 //                                         markerMap={this.markerMap}
@@ -575,7 +575,7 @@
 //                 <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
 //                     <SidebarAnnos
 //                         ref={this._sidebarRef}
-//                         {...this.props}
+//                         {...this._props}
 //                         fieldKey={this.fieldKey}
 //                         Document={this.Document}
 //                         layoutDoc={this.layoutDoc}
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 8760c8600..34e237007 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -18,10 +18,10 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     get mapBoxView() {
-        return this.props.DocumentView?.()?._props.docViewPath().lastElement()?.ComponentView as MapBox;
+        return this._props.DocumentView?.()?._props.docViewPath().lastElement()?.ComponentView as MapBox;
     }
     get mapBox() {
-        return this.props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
+        return this._props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
     }
 
     render() {
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index 70037f29c..2c31bbab7 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -89,7 +89,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
         return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this._props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
     @computed get SidebarKey() {
         return this.fieldKey + '_sidebar';
@@ -97,7 +97,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
 
     componentDidMount() {
         this._unmounting = false;
-        this.props.setContentView?.(this);
+        this._props.setContentView?.(this);
     }
 
     _unmounting = false;
@@ -162,9 +162,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
             e,
             (e, down, delta) =>
                 runInAction(() => {
-                    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 fullWidth = NumCast(this.layoutDoc._width);
                     const mapWidth = fullWidth - this.sidebarWidth();
@@ -183,7 +183,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
             () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
         );
     };
-    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth();
 
     /**
      * Handles toggle of sidebar on click the little comment button
@@ -195,7 +195,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
                 key="sidebar"
                 title="Toggle Sidebar"
                 style={{
-                    display: !this.props.isContentActive() ? 'none' : undefined,
+                    display: !this._props.isContentActive() ? 'none' : undefined,
                     top: StrCast(this.Document._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
@@ -232,12 +232,12 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
             FormattedTextBox.SetSelectOnLoad(target);
             return target;
         };
-        const docView = this.props.DocumentView?.();
+        const docView = this._props.DocumentView?.();
         docView &&
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(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.Document;
+                        e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this._props.Document;
                         e.annoDragData.linkSourceDoc.followLinkZoom = false;
                     }
                 },
@@ -276,15 +276,15 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey);
 
-    pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
+    pointerEvents = () => (this._props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
 
-    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
-    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
-    transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
-    opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
-    infoWidth = () => this.props.PanelWidth() / 5;
-    infoHeight = () => this.props.PanelHeight() / 5;
+    panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1) - this.sidebarWidth();
+    panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
+    scrollXf = () => this._props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
+    transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter];
+    opaqueFilter = () => [...this._props.childFilters(), Utils.OpaqueBackgroundFilter];
+    infoWidth = () => this._props.PanelWidth() / 5;
+    infoHeight = () => this._props.PanelHeight() / 5;
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     savedAnnotations = () => this._savedAnnotations;
 
@@ -399,9 +399,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
         MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
 
         const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude));
-        const x = point.x + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
-        const y = point.y + this.props.PanelHeight() / 2 + 32;
-        const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
+        const x = point.x + (this._props.PanelWidth() - this.sidebarWidth()) / 2;
+        const y = point.y + this._props.PanelHeight() / 2 + 32;
+        const cpt = this._props.ScreenToLocalTransform().inverse().transformPoint(x, y);
         MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true);
 
         document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
@@ -412,7 +412,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
      */
     @action
     mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
-        this.props.select(false);
+        this._props.select(false);
         this.deselectPin();
     };
     /*
@@ -677,9 +677,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
                 let target = document.elementFromPoint(e.x, e.y);
                 while (target) {
                     if (target === this._ref.current) {
-                        const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-                        const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2;
-                        const y = cpt[1] - 32 /* height of search bar */ - this.props.PanelHeight() / 2;
+                        const cpt = this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+                        const x = cpt[0] - (this._props.PanelWidth() - this.sidebarWidth()) / 2;
+                        const y = cpt[1] - 32 /* height of search bar */ - this._props.PanelHeight() / 2;
                         const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y));
                         this.createPushpin(location.latitude, location.longitude);
                         break;
@@ -776,8 +776,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
                                   .map((pushpin, i) => (
                                       <DocumentView
                                           key={i}
-                                          {...this.props}
-                                          renderDepth={this.props.renderDepth + 1}
+                                          {...this._props}
+                                          renderDepth={this._props.renderDepth + 1}
                                           Document={pushpin}
                                           PanelWidth={returnOne}
                                           PanelHeight={returnOne}
@@ -800,7 +800,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
                     </div>
                     {/* <MapBoxInfoWindow
                                     key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
-                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                    {...OmitKeys(this._props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                                     place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})}
                                     markerMap={this.markerMap}
                                     PanelWidth={this.infoWidth}
@@ -814,7 +814,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
                 <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
-                        {...this.props}
+                        {...this._props}
                         fieldKey={this.fieldKey}
                         Document={this.Document}
                         layoutDoc={this.layoutDoc}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
index 135db64e0..ae674d604 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -81,14 +81,14 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
     // semi-Constants
     xMin = 0;
     yMin = 0;
-    xMax = this.props.PanelWidth() * 0.6;
-    yMax = this.props.PanelHeight();
+    xMax = this._props.PanelWidth() * 0.6;
+    yMax = this._props.PanelHeight();
     color = `rgba(0,0,0,0.5)`;
     radius = 50;
     wallPositions: IWallProps[] = [];
 
     @computed get circularMotionRadius() {
-        return (NumCast(this.dataDoc.circularMotionRadius, 150) * this.props.PanelWidth()) / 1000;
+        return (NumCast(this.dataDoc.circularMotionRadius, 150) * this._props.PanelWidth()) / 1000;
     }
     @computed get gravity() {
         return NumCast(this.dataDoc.simulation_gravity, -9.81);
@@ -191,22 +191,22 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
 
     componentDidMount() {
         // Setup and update simulation
-        this._widthDisposer = reaction(() => [this.props.PanelWidth(), this.props.PanelHeight()], this.setupSimulation, { fireImmediately: true });
+        this._widthDisposer = reaction(() => [this._props.PanelWidth(), this._props.PanelHeight()], this.setupSimulation, { fireImmediately: true });
 
         // Create walls
         this.wallPositions = [
             { length: 100, xPos: 0, yPos: 0, angleInDegrees: 0 },
             { length: 100, xPos: 0, yPos: 100, angleInDegrees: 0 },
             { length: 100, xPos: 0, yPos: 0, angleInDegrees: 90 },
-            { length: 100, xPos: (this.xMax / this.props.PanelWidth()) * 100, yPos: 0, angleInDegrees: 90 },
+            { length: 100, xPos: (this.xMax / this._props.PanelWidth()) * 100, yPos: 0, angleInDegrees: 90 },
         ];
     }
 
-    componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<FieldViewProps>>) {
+    componentDidUpdate(prevProps: Readonly<FieldViewProps>) {
         super.componentDidUpdate(prevProps);
-        if (this.xMax !== this.props.PanelWidth() * 0.6 || this.yMax != this.props.PanelHeight()) {
-            this.xMax = this.props.PanelWidth() * 0.6;
-            this.yMax = this.props.PanelHeight();
+        if (this.xMax !== this._props.PanelWidth() * 0.6 || this.yMax != this._props.PanelHeight()) {
+            this.xMax = this._props.PanelWidth() * 0.6;
+            this.yMax = this._props.PanelHeight();
             this.setupSimulation();
         }
     }
@@ -632,7 +632,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
 
     // Default setup for pendulum simulation
     setupPendulum = () => {
-        const length = (300 * this.props.PanelWidth()) / 1000;
+        const length = (300 * this._props.PanelWidth()) / 1000;
         const angle = 30;
         const x = length * Math.cos(((90 - angle) * Math.PI) / 180);
         const y = length * Math.sin(((90 - angle) * Math.PI) / 180);
@@ -808,8 +808,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
         const commonWeightProps = {
             pause: this.pause,
             paused: BoolCast(this.dataDoc.simulation_paused),
-            panelWidth: this.props.PanelWidth,
-            panelHeight: this.props.PanelHeight,
+            panelWidth: this._props.PanelWidth,
+            panelHeight: this._props.PanelHeight,
             resetRequest: this.resetRequest,
             xMax: this.xMax,
             xMin: this.xMin,
@@ -853,9 +853,9 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
                                     <div
                                         style={{
                                             position: 'fixed',
-                                            left: 0.1 * this.props.PanelWidth() + 'px',
-                                            top: 0.95 * this.props.PanelHeight() + 'px',
-                                            width: 0.5 * this.props.PanelWidth() + 'px',
+                                            left: 0.1 * this._props.PanelWidth() + 'px',
+                                            top: 0.95 * this._props.PanelHeight() + 'px',
+                                            width: 0.5 * this._props.PanelWidth() + 'px',
                                         }}>
                                         <LinearProgress />
                                     </div>
@@ -923,8 +923,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
                     </div>
                     <div
                         className="mechanicsSimulationEquationContainer"
-                        onWheel={e => this.props.isContentActive() && e.stopPropagation()}
-                        style={{ overflow: 'auto', height: `${Math.max(1, 800 / this.props.PanelWidth()) * 100}%`, transform: `scale(${Math.min(1, this.props.PanelWidth() / 850)})` }}>
+                        onWheel={e => this._props.isContentActive() && e.stopPropagation()}
+                        style={{ overflow: 'auto', height: `${Math.max(1, 800 / this._props.PanelWidth()) * 100}%`, transform: `scale(${Math.min(1, this._props.PanelWidth() / 850)})` }}>
                         <div className="mechanicsSimulationControls">
                             <Stack direction="row" spacing={1}>
                                 {this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && (
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 658cfb1ca..c04a81f7d 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -31,7 +31,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
 
     componentDidMount() {
-        this.props.setContentView?.(this);
+        this._props.setContentView?.(this);
         Doc.SetNativeWidth(this.dataDoc, 1280);
         Doc.SetNativeHeight(this.dataDoc, 720);
     }
@@ -49,7 +49,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.dataDoc[this.fieldKey + '_duration'] = this.videoDuration;
 
         this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey);
-        this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.client);
+        this.dataDoc[this._props.fieldKey] = new VideoField(this.result.accessPaths.client);
         this.dataDoc[this.fieldKey + '_recorded'] = true;
         // stringify the presentation and store it
         if (presentation?.movements) {
@@ -135,7 +135,7 @@ 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.Document);
+        (ffView?.ComponentView as CollectionFreeFormView)._props.addDocument?.(value.Document);
         Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value.Document);
         Doc.RemFromMyOverlay(value.Document);
         Doc.UserDoc().currentRecording = undefined;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 79ed69cdd..36527c311 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -47,8 +47,8 @@ declare class MediaRecorder {
 //     _mesh: any = undefined;
 
 //     render() {
-//         const topLeft = [this.props.x, this.props.y];
-//         const raised = this.props.raised;
+//         const topLeft = [this._props.x, this._props.y];
+//         const raised = this._props.raised;
 //         const find = (raised: { coord: Vector2, off: Vector3 }[], what: Vector2) => raised.find(r => r.coord.x === what.x && r.coord.y === what.y);
 //         const tl1 = find(raised, new Vector2(topLeft[0], topLeft[1] + 1));
 //         const tl2 = find(raised, new Vector2(topLeft[0] + 1, topLeft[1] + 1));
@@ -69,11 +69,11 @@ declare class MediaRecorder {
 //         const normals = new Float32Array(quad_normals);
 //         const uvs = new Float32Array(quad_uvs); // Each vertex has one uv coordinate for texture mapping
 //         const indices = new Uint32Array(quad_indices);          // Use the four vertices to draw the two triangles that make up the square.
-//         const popOut = () => NumCast(this.props.Document.popOut);
-//         const popOff = () => NumCast(this.props.Document.popOff);
+//         const popOut = () => NumCast(this.Document.popOut);
+//         const popOff = () => NumCast(this.Document.popOff);
 //         return (
 //             <mesh key={`mesh${topLeft[0]}${topLeft[1]}`} onClick={action(async e => {
-//                 this.props.setRaised([
+//                 this._props.setRaised([
 //                     { coord: new Vector2(topLeft[0], topLeft[1]), off: new Vector3(-popOff(), -popOff(), popOut()) },
 //                     { coord: new Vector2(topLeft[0] + 1, topLeft[1]), off: new Vector3(popOff(), -popOff(), popOut()) },
 //                     { coord: new Vector2(topLeft[0], topLeft[1] + 1), off: new Vector3(-popOff(), popOff(), popOut()) },
@@ -99,7 +99,7 @@ declare class MediaRecorder {
 //                     r?.setAttribute('uv', new BufferAttribute(uvs, 2));
 //                     r?.setIndex(new BufferAttribute(indices, 1));
 //                 }} />
-//                 {!this._videoRef ? <meshStandardMaterial color={this.props.color} /> :
+//                 {!this._videoRef ? <meshStandardMaterial color={this._props.color} /> :
 //                     <meshBasicMaterial >
 //                         <videoTexture attach="map" args={[this._videoRef]} />
 //                     </meshBasicMaterial>}
@@ -118,7 +118,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     @observable private _videoRef: HTMLVideoElement | null = null;
     @observable _screenCapture = false;
     @computed get recordingStart() {
-        return Cast(this.dataDoc[this.props.fieldKey + '_recordingStart'], DateField)?.date.getTime();
+        return Cast(this.dataDoc[this._props.fieldKey + '_recordingStart'], DateField)?.date.getTime();
     }
 
     constructor(props: any) {
@@ -144,8 +144,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     componentDidMount() {
         this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
-        this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
-        // this.layoutDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+        this._props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+        // this.layoutDoc.videoWall && reaction(() => ({ width: this._props.PanelWidth(), height: this._props.PanelHeight() }),
         //     ({ width, height }) => {
         //         if (this._camera) {
         //             const angle = -Math.abs(1 - width / height);
@@ -201,7 +201,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         //     let count = 0;
         //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
         //         <VideoTile doc={this.layoutDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
-        //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
+        //     return <Canvas key="canvas" id="CANCAN" style={{ width: this._props.PanelWidth(), height: this._props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
         //         this._camera = props.camera;
         //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
         //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
@@ -224,15 +224,15 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             this._audioRec.onstop = async (e: any) => {
                 const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({ file })));
                 if (!(result instanceof Error)) {
-                    this.dataDoc[this.props.fieldKey + '_audio'] = new AudioField(result.accessPaths.agnostic.client);
+                    this.dataDoc[this._props.fieldKey + '_audio'] = new AudioField(result.accessPaths.agnostic.client);
                 }
             };
             this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
             this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
             const vid_chunks: any = [];
             this._videoRec.onstart = () => {
-                if (this.dataDoc[this.props.fieldKey + '_trackScreen']) TrackMovements.Instance.start();
-                this.dataDoc[this.props.fieldKey + '_recordingStart'] = new DateField(new Date());
+                if (this.dataDoc[this._props.fieldKey + '_trackScreen']) TrackMovements.Instance.start();
+                this.dataDoc[this._props.fieldKey + '_recordingStart'] = new DateField(new Date());
             };
             this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
             this._videoRec.onstop = async (e: any) => {
@@ -252,7 +252,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                     this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
                     this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
                     this.layoutDoc._layout_fitWidth = undefined;
-                    this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
+                    this.dataDoc[this._props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
                 } else alert('video conversion failed');
             };
             this._audioRec.start();
@@ -287,8 +287,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         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();
-    formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
+    videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc._height)) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc._width))) * this._props.PanelWidth();
+    formattedPanelHeight = () => Math.max(0, this._props.PanelHeight() - this.videoPanelHeight());
     render() {
         TraceMobx();
         return (
@@ -296,14 +296,14 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                 <div className="videoBox-viewer">
                     <div style={{ position: 'relative', height: this.videoPanelHeight() }}>
                         <CollectionFreeFormView
-                            {...this.props}
+                            {...this._props}
                             setContentView={emptyFunction}
                             NativeWidth={returnZero}
                             NativeHeight={returnZero}
                             PanelHeight={this.videoPanelHeight}
-                            PanelWidth={this.props.PanelWidth}
-                            focus={this.props.focus}
-                            isSelected={this.props.isSelected}
+                            PanelWidth={this._props.PanelWidth}
+                            focus={this._props.focus}
+                            isSelected={this._props.isSelected}
                             isAnnotationOverlay={true}
                             select={emptyFunction}
                             isContentActive={returnFalse}
@@ -313,8 +313,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                             removeDocument={returnFalse}
                             moveDocument={returnFalse}
                             addDocument={returnFalse}
-                            ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                            renderDepth={this.props.renderDepth + 1}>
+                            ScreenToLocalTransform={this._props.ScreenToLocalTransform}
+                            renderDepth={this._props.renderDepth + 1}>
                             <>
                                 {this.threed}
                                 {this.content}
@@ -324,7 +324,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                     <div style={{ background: SettingsManager.userColor, position: 'relative', height: this.formattedPanelHeight() }}>
                         {!(this.dataDoc[this.fieldKey + '_dictation'] instanceof Doc) ? null : (
                             <FormattedTextBox
-                                {...this.props}
+                                {...this._props}
                                 Document={DocCast(this.dataDoc[this.fieldKey + '_dictation'])}
                                 fieldKey={'text'}
                                 PanelHeight={this.formattedPanelHeight}
@@ -337,12 +337,12 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                                 removeDocument={returnFalse}
                                 moveDocument={returnFalse}
                                 addDocument={returnFalse}
-                                renderDepth={this.props.renderDepth + 1}
+                                renderDepth={this._props.renderDepth + 1}
                             />
                         )}
                     </div>
                 </div>
-                {!this.props.isSelected() ? null : (
+                {!this._props.isSelected() ? null : (
                     <div className="screenshotBox-uiButtons" style={{ background: SettingsManager.userColor }}>
                         <div className="screenshotBox-recorder" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userVariantColor }} key="snap" onPointerDown={this.toggleRecording}>
                             <FontAwesomeIcon icon="file" size="lg" />
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 8e506ec64..73ad3a004 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -60,7 +60,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         super(props);
         makeObservable(this);
         if (!this.compileParams.length) {
-            const params = ScriptCast(this.dataDoc[this.props.fieldKey])?.script.options.params as { [key: string]: any };
+            const params = ScriptCast(this.dataDoc[this._props.fieldKey])?.script.options.params as { [key: string]: any };
             if (params) {
                 this.compileParams = Array.from(Object.keys(params))
                     .filter(p => !p.startsWith('_'))
@@ -116,7 +116,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
     @action
     componentDidMount() {
-        this.props.setContentView?.(this);
+        this._props.setContentView?.(this);
         this.rawText = this.rawScript;
         const observer = new _global.ResizeObserver(
             action((entries: any) => {
@@ -294,7 +294,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
     onCopy = () => {
         const copy = Doc.MakeCopy(this.Document, true);
         copy.x = NumCast(this.Document.x) + NumCast(this.dataDoc._width);
-        this.props.addDocument?.(copy);
+        this._props.addDocument?.(copy);
     };
 
     // adds option to create a copy to the context menu
@@ -310,7 +310,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         const nameInput = <textarea className="scriptingBox-textarea-inputs" onChange={e => (this.functionName = e.target.value)} placeholder="enter name here" value={this.functionName} />;
 
         return (
-            <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected() && e.stopPropagation()}>
+            <div className="scriptingBox-inputDiv" onPointerDown={e => this._props.isSelected() && e.stopPropagation()}>
                 <div className="scriptingBox-wrapper" style={{ maxWidth: '100%' }}>
                     <div className="container" style={{ maxWidth: '100%' }}>
                         <div className="descriptor" style={{ textAlign: 'center', display: 'inline-block', maxWidth: '100%' }}>
@@ -687,7 +687,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         );
 
         return (
-            <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected() && e.stopPropagation()}>
+            <div className="scriptingBox-inputDiv" onPointerDown={e => this._props.isSelected() && e.stopPropagation()}>
                 <div className="scriptingBox-wrapper">
                     {this.renderScriptingBox}
                     {definedParameters}
@@ -745,7 +745,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() && 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) => (
@@ -812,7 +812,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() && 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/audio/AudioWaveform.tsx b/src/client/views/nodes/audio/AudioWaveform.tsx
index 3e46ff4e6..7fd799952 100644
--- a/src/client/views/nodes/audio/AudioWaveform.tsx
+++ b/src/client/views/nodes/audio/AudioWaveform.tsx
@@ -109,7 +109,7 @@ export class AudioWaveform extends ObservableReactComponent<AudioWaveformProps>
                     progressColor={Colors.MEDIUM_BLUE_ALT}
                     progress={this._props.progress ?? 1}
                     barWidth={200 / this.audioBuckets.length}
-                    //gradientColors={this.props.gradientColors}
+                    //gradientColors={this._props.gradientColors}
                     peaks={this.audioBuckets}
                     width={(this._props.PanelWidth ?? 0) * window.devicePixelRatio}
                     height={this.waveHeight * window.devicePixelRatio}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 1002ee403..5a13fa8b4 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -177,7 +177,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
     };
 
     componentWillUnmount = () => Object.values(this._disposers).forEach(disposer => disposer?.());
-    isContentActive = () => this.props.tbox.props.isSelected() || this.props.tbox.isAnyChildContentActive?.();
+    isContentActive = () => this.props.tbox._props.isSelected() || this.props.tbox.isAnyChildContentActive?.();
 
     render() {
         return !this._dashDoc || !this._finalLayout || this.props.hidden ? null : (
@@ -205,21 +205,21 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     removeDocument={this.removeDoc}
                     isDocumentActive={returnFalse}
                     isContentActive={this.isContentActive}
-                    styleProvider={this._textBox.props.styleProvider}
-                    docViewPath={this._textBox.props.docViewPath}
+                    styleProvider={this._textBox._props.styleProvider}
+                    docViewPath={this._textBox._props.docViewPath}
                     ScreenToLocalTransform={this.getDocTransform}
-                    addDocTab={this._textBox.props.addDocTab}
+                    addDocTab={this._textBox._props.addDocTab}
                     pinToPres={returnFalse}
-                    renderDepth={this._textBox.props.renderDepth + 1}
+                    renderDepth={this._textBox._props.renderDepth + 1}
                     PanelWidth={this._finalLayout[Width]}
                     PanelHeight={this._finalLayout[Height]}
                     focus={this.outerFocus}
                     whenChildContentsActiveChanged={this.props.tbox.whenChildContentsActiveChanged}
                     bringToFront={emptyFunction}
                     dontRegisterView={false}
-                    childFilters={this.props.tbox?.props.childFilters}
-                    childFiltersByRanges={this.props.tbox?.props.childFiltersByRanges}
-                    searchFilterDocs={this.props.tbox?.props.searchFilterDocs}
+                    childFilters={this.props.tbox?._props.childFilters}
+                    childFiltersByRanges={this.props.tbox?._props.childFiltersByRanges}
+                    searchFilterDocs={this.props.tbox?._props.searchFilterDocs}
                 />
             </div>
         );
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 7e655531e..b786c5ffb 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -65,8 +65,8 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
 
     constructor(props: any) {
         super(props);
-        this._fieldKey = this.props.fieldKey;
-        this._textBoxDoc = this.props.tbox.props.Document;
+        this._fieldKey = props.fieldKey;
+        this._textBoxDoc = props.tbox.Document;
     }
 
     componentWillUnmount() {
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 3b31f2d17..5858c3b11 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -186,7 +186,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveAlignment() {
-        if (this.view && this.TextView?.props.rootSelected?.()) {
+        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) {
@@ -199,7 +199,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveListStyle() {
-        if (this.view && this.TextView?.props.rootSelected?.()) {
+        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) {
@@ -219,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.rootSelected?.()) {
+        if (this.view && this.TextView?._props.rootSelected?.()) {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
             const marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -254,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.rootSelected?.()) 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);
@@ -447,7 +447,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         this.layoutDoc && (this.layoutDoc.layout_centered = !this.layoutDoc.layout_centered);
     };
     align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
-        if (this.TextView?.props.rootSelected?.()) {
+        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)) {
@@ -581,7 +581,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return (this.view as any)?.TextView as FormattedTextBox;
     }
     get TextViewFieldKey() {
-        return this.TextView?.props.fieldKey;
+        return this.TextView?._props.fieldKey;
     }
 
     @action setActiveHighlight(color: string) {
@@ -774,11 +774,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         //         <div className="collectionMenu-divider" key="divider 3" />
         //         {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
         //             this.activeFontSize = val;
-        //             SelectionManager.Views.map(dv => dv.props.Document._text_fontSize = val);
+        //             SelectionManager.Views.map(dv => dv.Document._text_fontSize = val);
         //         })),
         //         this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
         //             this.activeFontFamily = val;
-        //             SelectionManager.Views.map(dv => dv.props.Document._text_fontFamily = val);
+        //             SelectionManager.Views.map(dv => dv.Document._text_fontFamily = val);
         //         })),
         //         <div className="collectionMenu-divider" key="divider 4" />,
         //         this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index b573f7c48..1a92acea1 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -13,12 +13,12 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return FieldView.LayoutString(ImportElementBox, fieldKey);
     }
 
-    screenToLocalXf = () => this.props.ScreenToLocalTransform().scale(1 * (this.props.NativeDimScaling?.() || 1));
+    screenToLocalXf = () => this._props.ScreenToLocalTransform().scale(1 * (this._props.NativeDimScaling?.() || 1));
     @computed get mainItem() {
         return (
             <div style={{ backgroundColor: 'pink' }}>
                 <DocumentView
-                    {...this.props} //
+                    {...this._props} //
                     LayoutTemplateString={undefined}
                     Document={this.Document}
                     isContentActive={returnFalse}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 5ea9e9979..a1f5ce703 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.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, Opt } from '../../../fields/Doc';
@@ -12,6 +12,7 @@ import { OpenWhere } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { AnchorMenu } from './AnchorMenu';
 import './Annotation.scss';
+import { ObservableReactComponent } from '../ObservableReactComponent';
 
 interface IAnnotationProps extends FieldViewProps {
     anno: Doc;
@@ -21,12 +22,16 @@ interface IAnnotationProps extends FieldViewProps {
     pointerEvents?: () => Opt<string>;
 }
 @observer
-export class Annotation extends React.Component<IAnnotationProps> {
+export class Annotation extends ObservableReactComponent<IAnnotationProps> {
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+    }
     render() {
         return (
-            <div style={{ display: this.props.anno.textCopied && !Doc.GetBrushHighlightStatus(this.props.anno) ? 'none' : undefined }}>
-                {DocListCast(this.props.anno.text_inlineAnnotations).map(a => (
-                    <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
+            <div style={{ display: this._props.anno.textCopied && !Doc.GetBrushHighlightStatus(this._props.anno) ? 'none' : undefined }}>
+                {DocListCast(this._props.anno.text_inlineAnnotations).map(a => (
+                    <RegionAnnotation pointerEvents={this._props.pointerEvents} {...this._props} document={a} key={a[Id]} />
                 ))}
             </div>
         );
@@ -38,23 +43,23 @@ interface IRegionAnnotationProps extends IAnnotationProps {
     pointerEvents?: () => Opt<string>;
 }
 @observer
-class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
+class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps> {
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
     @computed get annoTextRegion() {
-        return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document;
+        return Cast(this._props.document.annoTextRegion, Doc, null) || this._props.document;
     }
 
     @undoBatch
     deleteAnnotation = () => {
-        const docAnnotations = DocListCast(this.props.dataDoc[this.props.fieldKey]);
-        this.props.dataDoc[this.props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
+        const docAnnotations = DocListCast(this._props.dataDoc[this._props.fieldKey]);
+        this._props.dataDoc[this._props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
         AnchorMenu.Instance.fadeOut(true);
-        this.props.select(false);
+        this._props.select(false);
     };
 
     @undoBatch
-    pinToPres = () => this.props.pinToPres(this.annoTextRegion, {});
+    pinToPres = () => this._props.pinToPres(this.annoTextRegion, {});
 
     @undoBatch
     makeTargretToggle = () => (this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle);
@@ -65,7 +70,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         const trail = DocCast(anchor.presentationTrail);
         if (trail) {
             Doc.ActivePresentation = trail;
-            this.props.addDocTab(trail, OpenWhere.replaceRight);
+            this._props.addDocTab(trail, OpenWhere.replaceRight);
         }
     };
 
@@ -94,9 +99,9 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     };
 
     @computed get linkHighlighted() {
-        for (const link of LinkManager.Instance.getAllDirectLinks(this.props.document)) {
-            const a1 = LinkManager.getOppositeAnchor(link, this.props.document);
-            if (a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, this.props.document))) return true;
+        for (const link of LinkManager.Instance.getAllDirectLinks(this._props.document)) {
+            const a1 = LinkManager.getOppositeAnchor(link, this._props.document);
+            if (a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, this._props.document))) return true;
         }
     }
 
@@ -107,24 +112,24 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                 className="htmlAnnotation"
                 ref={this._mainCont}
                 onPointerEnter={action(() => {
-                    Doc.BrushDoc(this.props.anno);
-                    this.props.showInfo?.(this.props.anno);
+                    Doc.BrushDoc(this._props.anno);
+                    this._props.showInfo?.(this._props.anno);
                 })}
                 onPointerLeave={action(() => {
-                    Doc.UnBrushDoc(this.props.anno);
-                    this.props.showInfo?.(undefined);
+                    Doc.UnBrushDoc(this._props.anno);
+                    this._props.showInfo?.(undefined);
                 })}
                 onPointerDown={this.onPointerDown}
                 onContextMenu={this.onContextMenu}
                 style={{
-                    left: NumCast(this.props.document.x),
-                    top: NumCast(this.props.document.y),
-                    width: NumCast(this.props.document._width),
-                    height: NumCast(this.props.document._height),
+                    left: NumCast(this._props.document.x),
+                    top: NumCast(this._props.document.y),
+                    width: NumCast(this._props.document._width),
+                    height: NumCast(this._props.document._height),
                     opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
-                    pointerEvents: this.props.pointerEvents?.() as any,
+                    pointerEvents: this._props.pointerEvents?.() as any,
                     outline: brushed === Doc.DocBrushStatus.unbrushed && this.linkHighlighted ? 'solid 1px lightBlue' : undefined,
-                    backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this.props.document.backgroundColor),
+                    backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this._props.document.backgroundColor),
                 }}
             />
         );
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 14187833f..4d29573d4 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -122,10 +122,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
     @undoBatch
     makeLink = action((linkTo: Doc) => {
-        const linkFrom = this.props.linkCreateAnchor?.();
+        const linkFrom = this._props.linkCreateAnchor?.();
         if (linkFrom) {
             const link = DocUtils.MakeLink(linkFrom, linkTo, {});
-            link && this.props.linkCreated?.(link);
+            link && this._props.linkCreated?.(link);
         }
     });
 
@@ -292,7 +292,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
-        if (!this.props.linkSearch) Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true));
+        if (!this._props.linkSearch) Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true));
         this._results.clear();
 
         if (query) {
@@ -375,13 +375,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     render() {
         var validResults = 0;
 
-        const isLinkSearch: boolean = this.props.linkSearch;
+        const isLinkSearch: boolean = this._props.linkSearch;
 
         const sortedResults = Array.from(this._results.entries()).sort((a, b) => (this._pageRanks.get(b[0]) ?? 0) - (this._pageRanks.get(a[0]) ?? 0)); // sorted by page rank
 
         const resultsJSX = Array();
 
-        const fromDoc = this.props.linkFrom?.();
+        const fromDoc = this._props.linkFrom?.();
 
         sortedResults.forEach(result => {
             var className = 'searchBox-results-scroll-view-result';
diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx
index daacb368b..39e778b76 100644
--- a/src/client/views/selectedDoc/SelectedDocView.tsx
+++ b/src/client/views/selectedDoc/SelectedDocView.tsx
@@ -1,14 +1,14 @@
+import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { ListBox } from 'browndash-components';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
-import * as React from 'react';
-import { emptyFunction } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { StrCast } from '../../../fields/Types';
 import { DocumentManager } from '../../util/DocumentManager';
-import { SettingsManager } from '../../util/SettingsManager';
 import { DocFocusOptions } from '../nodes/DocumentView';
+import { emptyFunction } from '../../../Utils';
+import { SettingsManager } from '../../util/SettingsManager';
 
 export interface SelectedDocViewProps {
     selectedDocs: Doc[];
-- 
cgit v1.2.3-70-g09d2


From af380979349308077e13fc12a2d09255b7f05f28 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 23 Jan 2024 16:11:42 -0500
Subject: reorganization of DocumentView, DocumentViewInternal and FieldView
 methods and props. fix for selection bug after following a link.  migrating
 to use [DocData] instad of GetProto()

---
 src/client/documents/Documents.ts                  |   15 +-
 src/client/util/CurrentUserUtils.ts                |    2 +-
 src/client/util/DocumentManager.ts                 |   19 +-
 src/client/util/DragManager.ts                     |    7 +-
 src/client/util/DropConverter.ts                   |    9 +-
 src/client/util/HypothesisUtils.ts                 |   18 +-
 src/client/util/LinkFollower.ts                    |    9 +-
 src/client/util/LinkManager.ts                     |   12 +-
 src/client/util/SearchUtil.ts                      |  155 +--
 src/client/util/SettingsManager.tsx                |    5 +-
 src/client/views/DashboardView.tsx                 |   12 +-
 src/client/views/DocComponent.tsx                  |  125 +-
 src/client/views/DocumentDecorations.tsx           |   16 +-
 src/client/views/InkingStroke.tsx                  |   14 +-
 src/client/views/LightboxView.tsx                  |    2 +-
 src/client/views/MainView.tsx                      |   29 +-
 src/client/views/MarqueeAnnotator.tsx              |    9 +-
 src/client/views/PropertiesView.tsx                |    5 +-
 src/client/views/ScriptBox.tsx                     |    5 +-
 src/client/views/SidebarAnnos.tsx                  |    9 +-
 src/client/views/StyleProvider.tsx                 |   12 +-
 src/client/views/TemplateMenu.tsx                  |    6 +-
 .../views/collections/CollectionCalendarView.tsx   |    4 +-
 .../views/collections/CollectionCarousel3DView.tsx |    7 +-
 .../views/collections/CollectionCarouselView.tsx   |    6 +-
 .../views/collections/CollectionDockingView.tsx    |   18 +-
 src/client/views/collections/CollectionMenu.tsx    |   25 +-
 .../views/collections/CollectionNoteTakingView.tsx |   20 +-
 .../collections/CollectionStackedTimeline.tsx      |   27 +-
 .../views/collections/CollectionStackingView.tsx   |   28 +-
 src/client/views/collections/CollectionSubView.tsx |   33 +-
 .../views/collections/CollectionTimeView.tsx       |    7 +-
 .../views/collections/CollectionTreeView.tsx       |    2 +-
 src/client/views/collections/CollectionView.tsx    |    7 +-
 src/client/views/collections/TabDocView.tsx        |   10 +-
 src/client/views/collections/TreeView.tsx          |   19 +-
 .../CollectionFreeFormLinkView.tsx                 |    6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   90 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |    7 +-
 .../collectionGrid/CollectionGridView.tsx          |    6 +-
 .../CollectionMulticolumnView.tsx                  |    4 +-
 .../CollectionMultirowView.tsx                     |    4 +-
 .../collectionMulticolumn/MulticolumnResizer.tsx   |    6 +-
 .../collectionMulticolumn/MultirowResizer.tsx      |    6 +-
 .../collectionSchema/CollectionSchemaView.tsx      |   10 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   10 +-
 .../collectionSchema/SchemaTableCell.tsx           |    5 +-
 src/client/views/linking/LinkMenu.tsx              |    2 +-
 src/client/views/linking/LinkMenuGroup.tsx         |   12 +-
 src/client/views/newlightbox/NewLightboxView.tsx   |    2 +-
 src/client/views/nodes/AudioBox.tsx                |   15 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   10 +-
 src/client/views/nodes/ComparisonBox.tsx           |   15 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |   16 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   29 +-
 src/client/views/nodes/DocumentIcon.tsx            |    2 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   40 +-
 src/client/views/nodes/DocumentView.tsx            | 1214 ++++++++------------
 src/client/views/nodes/EquationBox.tsx             |    4 +-
 src/client/views/nodes/FieldView.tsx               |  109 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |    2 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |    2 +-
 src/client/views/nodes/ImageBox.tsx                |   25 +-
 src/client/views/nodes/KeyValueBox.tsx             |    2 +-
 src/client/views/nodes/KeyValuePair.tsx            |    4 +-
 src/client/views/nodes/LabelBox.tsx                |   23 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |   10 +-
 src/client/views/nodes/LinkBox.tsx                 |    7 +-
 src/client/views/nodes/LinkDescriptionPopup.tsx    |   31 +-
 src/client/views/nodes/LinkDocPreview.tsx          |    6 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   16 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |   12 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |    5 +-
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |   26 +-
 src/client/views/nodes/PDFBox.tsx                  |   28 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   12 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   13 +-
 src/client/views/nodes/ScriptingBox.tsx            |    8 +-
 src/client/views/nodes/VideoBox.tsx                |   42 +-
 src/client/views/nodes/WebBox.tsx                  |   20 +-
 src/client/views/nodes/calendarBox/CalendarBox.tsx |   10 +-
 .../views/nodes/formattedText/DashDocView.tsx      |   12 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   45 +-
 .../views/nodes/importBox/ImportElementBox.tsx     |    6 +-
 src/client/views/nodes/trails/PresBox.tsx          |   37 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |    2 +-
 src/client/views/pdf/PDFViewer.tsx                 |    9 +-
 src/client/views/search/SearchBox.tsx              |    8 +-
 src/client/views/selectedDoc/SelectedDocView.tsx   |    8 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |   16 +-
 src/fields/Doc.ts                                  |   52 +-
 91 files changed, 1304 insertions(+), 1527 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 40cffcf45..bcc016391 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -205,6 +205,7 @@ export class DocumentOptions {
     type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection
     _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection
     title?: STRt = new StrInfo('title of document', true);
+    title_custom?: BOOLt = new BoolInfo('whether title is a default or has been intentionally set');
     caption?: RichTextField;
     author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable
     author_date?: DATEt = new DateInfo('date the document was created', true);
@@ -1454,17 +1455,17 @@ export namespace DocUtils {
                 TaskCompletionBox.popupY = showPopup[1] - 33;
                 TaskCompletionBox.taskCompleted = true;
 
-                LinkDescriptionPopup.popupX = showPopup[0];
-                LinkDescriptionPopup.popupY = showPopup[1];
-                LinkDescriptionPopup.descriptionPopup = true;
+                LinkDescriptionPopup.Instance.popupX = showPopup[0];
+                LinkDescriptionPopup.Instance.popupY = showPopup[1];
+                LinkDescriptionPopup.Instance.display = true;
 
                 const rect = document.body.getBoundingClientRect();
-                if (LinkDescriptionPopup.popupX + 200 > rect.width) {
-                    LinkDescriptionPopup.popupX -= 190;
+                if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) {
+                    LinkDescriptionPopup.Instance.popupX -= 190;
                     TaskCompletionBox.popupX -= 40;
                 }
-                if (LinkDescriptionPopup.popupY + 100 > rect.height) {
-                    LinkDescriptionPopup.popupY -= 40;
+                if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) {
+                    LinkDescriptionPopup.Instance.popupY -= 40;
                     TaskCompletionBox.popupY -= 40;
                 }
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 0b15e3add..c4af20b9b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -275,7 +275,7 @@ export class CurrentUserUtils {
             {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _layout_fitWidth: true, }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
             {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
-            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
+            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title_custom: true,  waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
             {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}},
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index e81a99e40..f730d17fe 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,6 +1,7 @@
+import { Howl } from 'howler';
 import { action, computed, makeObservable, observable, ObservableSet, observe } from 'mobx';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { AclAdmin, AclEdit, Animation } from '../../fields/DocSymbols';
+import { AclAdmin, AclEdit, Animation, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { listSpec } from '../../fields/Schema';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
@@ -10,13 +11,13 @@ import { CollectionViewType } from '../documents/DocumentTypes';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { TabDocView } from '../views/collections/TabDocView';
 import { LightboxView } from '../views/LightboxView';
-import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
+import { FocusViewOptions } from '../views/nodes/FieldView';
 import { KeyValueBox } from '../views/nodes/KeyValueBox';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
 import { PresBox } from '../views/nodes/trails';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
-import { Howl } from 'howler';
 
 export class DocumentManager {
     private static _instance: DocumentManager;
@@ -135,7 +136,7 @@ export class DocumentManager {
         });
         if (toReturn.length === 0) {
             DocumentManager.Instance.DocumentViews.forEach(view => {
-                if (Doc.GetProto(view.Document)?.[Id] === id) {
+                if (view.Document[DocData]?.[Id] === id) {
                     toReturn.push(view);
                 }
             });
@@ -236,7 +237,7 @@ export class DocumentManager {
     // shows a documentView by:
     // traverses down through the viewPath of contexts to the view:
     //           focusing on each context
-    public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => {
+    public showDocumentView = async (targetDocView: DocumentView, options: FocusViewOptions) => {
         const docViewPath = [...(targetDocView.containerViewPath?.() ?? []), targetDocView];
         let rootContextView = docViewPath.shift();
         await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined, focused: false })));
@@ -252,7 +253,7 @@ export class DocumentManager {
     //           and finally restoring the targetDoc to the viewSpec specified by the last document which may either be the targetDoc, or a viewSpec that describes the targetDoc configuration
     public showDocument = async (
         targetDoc: Doc, // document to display
-        options: DocFocusOptions, // options for how to navigate to target
+        options: FocusViewOptions, // options for how to navigate to target
         finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
     ) => {
         Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
@@ -293,7 +294,7 @@ export class DocumentManager {
 
     focusViewsInPath = async (
         docView: DocumentView, //
-        options: DocFocusOptions,
+        options: FocusViewOptions,
         iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt<Doc>; childDocView: Opt<DocumentView>; focused: boolean }>
     ) => {
         let contextView: DocumentView | undefined; // view containing context that contains target
@@ -313,7 +314,7 @@ export class DocumentManager {
     };
 
     @action
-    restoreDocView(viewSpec: Opt<Doc>, docView: DocumentView, options: DocFocusOptions, contextView: Opt<DocumentView>, targetDoc: Doc) {
+    restoreDocView(viewSpec: Opt<Doc>, docView: DocumentView, options: FocusViewOptions, contextView: Opt<DocumentView>, targetDoc: Doc) {
         if (viewSpec && docView) {
             //if (docView.ComponentView instanceof FormattedTextBox)
             //viewSpec !== docView.Document &&
@@ -337,7 +338,7 @@ export class DocumentManager {
         }
     }
 }
-export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
+export function DocFocusOrOpen(doc: Doc, options: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
     const func = () => {
         const cv = DocumentManager.Instance.getDocumentView(containingDoc);
         const dv = DocumentManager.Instance.getDocumentView(doc, cv);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9ede18ed5..70c40c54e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -13,6 +13,7 @@ import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
+import { DocData } from '../../fields/DocSymbols';
 const { default : { contextMenuZindex } } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
 
 export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
@@ -222,7 +223,7 @@ export namespace DragManager {
                                   : docDragData.dropAction === 'add'
                                     ? d
                                     : docDragData.dropAction === 'proto'
-                                      ? Doc.GetProto(d)
+                                      ? d[DocData]
                                       : docDragData.dropAction === 'copy'
                                         ? (await Doc.MakeClone(d)).clone
                                         : d
@@ -250,9 +251,9 @@ export namespace DragManager {
     export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
         const finishDrag = (e: DragCompleteEvent) => {
             const bd = Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
-            params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields
+            params.map(p => Object.keys(vars).indexOf(p) !== -1 && (bd[DocData][p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields
             initialize?.(bd);
-            Doc.GetProto(bd)['onClick-paramFieldKeys'] = new List<string>(params);
+            bd[DocData]['onClick-paramFieldKeys'] = new List<string>(params);
             e.docDragData && (e.docDragData.droppedDocuments = [bd]);
             return e;
         };
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 2c371f28e..f62ec8f83 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -1,4 +1,5 @@
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
+import { DocData } from '../../fields/DocSymbols';
 import { ObjectField } from '../../fields/ObjectField';
 import { RichTextField } from '../../fields/RichTextField';
 import { listSpec } from '../../fields/Schema';
@@ -12,7 +13,7 @@ import { DragManager } from './DragManager';
 import { ScriptingGlobals } from './ScriptingGlobals';
 
 export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined, templateField: string = '') {
-    if (templateField) Doc.GetProto(doc).title = templateField; /// the title determines which field is being templated
+    if (templateField) doc[DocData].title = templateField; /// the title determines which field is being templated
     doc.isTemplateDoc = makeTemplate(doc, first, rename);
     return doc;
 }
@@ -33,7 +34,7 @@ function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = und
     let any = false;
     docs.forEach(d => {
         if (!StrCast(d.title).startsWith('-')) {
-            any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any;
+            any = Doc.MakeMetadataFieldTemplate(d, layoutDoc[DocData]) || any;
         } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) {
             any = makeTemplate(d, false) || any;
         }
@@ -41,12 +42,12 @@ function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = und
     if (first) {
         if (!docs.length) {
             // bcz: feels hacky : if the root level document has items, it's not a field template
-            any = Doc.MakeMetadataFieldTemplate(doc, Doc.GetProto(layoutDoc)) || any;
+            any = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData]) || any;
         }
     }
     if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
         if (!StrCast(layoutDoc.title).startsWith('-')) {
-            any = Doc.MakeMetadataFieldTemplate(layoutDoc, Doc.GetProto(layoutDoc));
+            any = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData]);
         }
     }
     rename && (doc.title = rename);
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index f46c2d431..c5f307f44 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -30,15 +30,15 @@ export namespace Hypothesis {
         if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
 
         const results: Doc[] = [];
-        await SearchUtil.Search('web', true).then(
-            action(async (res: SearchUtil.DocSearchResult) => {
-                const docs = res.docs;
-                const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
-                filteredDocs.forEach(doc => {
-                    uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
-                });
-            })
-        );
+        // await SearchUtil.Search('web', true).then(
+        //     action(async (res: SearchUtil.DocSearchResult) => {
+        //         const docs = res.docs;
+        //         const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
+        //         filteredDocs.forEach(doc => {
+        //             uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
+        //         });
+        //     })
+        // );
 
         const onScreenResults = results.filter(doc => DocumentManager.Instance.getFirstDocumentView(doc));
         return onScreenResults.length ? onScreenResults[0] : results.length ? results[0] : undefined; // prioritize results that are currently on the screen
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 2df4d1ca8..20261859c 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,16 +1,17 @@
-import { action, observable, runInAction } from 'mobx';
+import { action, runInAction } from 'mobx';
 import { Doc, DocListCast, Field, FieldResult, Opt } from '../../fields/Doc';
 import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
-import { DocFocusOptions, OpenWhere } from '../views/nodes/DocumentView';
+import { OpenWhere } from '../views/nodes/DocumentView';
+import { FocusViewOptions } from '../views/nodes/FieldView';
 import { PresBox } from '../views/nodes/trails';
 import { DocumentManager } from './DocumentManager';
 import { LinkManager } from './LinkManager';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
-import { UndoManager } from './UndoManager';
 import { SnappingManager } from './SnappingManager';
+import { UndoManager } from './UndoManager';
 /*
  * link doc:
  * - link_anchor_1: doc
@@ -72,7 +73,7 @@ export class LinkFollower {
             if (target) {
                 const doFollow = (canToggle?: boolean) => {
                     const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle);
-                    const options: DocFocusOptions = {
+                    const options: FocusViewOptions = {
                         playAudio: BoolCast(srcAnchor.followLinkAudio),
                         playMedia: BoolCast(srcAnchor.followLinkVideo),
                         toggleTarget,
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index ccb3c6b98..353f28a92 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,7 @@
 import { action, makeObservable, observable, observe, runInAction } from 'mobx';
 import { computedFn } from 'mobx-utils';
 import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
-import { DirectLinks } from '../../fields/DocSymbols';
+import { DirectLinks, DocData } from '../../fields/DocSymbols';
 import { FieldLoader } from '../../fields/FieldLoader';
 import { List } from '../../fields/List';
 import { ProxyField } from '../../fields/Proxy';
@@ -58,8 +58,8 @@ export class LinkManager {
                                 link &&
                                     action(lAnchProtoProtos => {
                                         Doc.AddDocToList(Doc.UserDoc(), 'links', link);
-                                        lAnchs[0] && Doc.GetProto(lAnchs[0])[DirectLinks].add(link);
-                                        lAnchs[1] && Doc.GetProto(lAnchs[1])[DirectLinks].add(link);
+                                        lAnchs[0] && lAnchs[0][DocData][DirectLinks].add(link);
+                                        lAnchs[1] && lAnchs[1][DocData][DirectLinks].add(link);
                                     })
                             )
                         )
@@ -74,8 +74,8 @@ export class LinkManager {
                         Promise.all(lAnchs.map(lAnch => PromiseValue(lAnch?.proto as Doc))).then((lAnchProtos: Opt<Doc>[]) =>
                             Promise.all(lAnchProtos.map(lAnchProto => PromiseValue(lAnchProto?.proto as Doc))).then(
                                 action(lAnchProtoProtos => {
-                                    link && lAnchs[0] && Doc.GetProto(lAnchs[0])[DirectLinks].delete(link);
-                                    link && lAnchs[1] && Doc.GetProto(lAnchs[1])[DirectLinks].delete(link);
+                                    link && lAnchs[0] && lAnchs[0][DocData][DirectLinks].delete(link);
+                                    link && lAnchs[1] && lAnchs[1][DocData][DirectLinks].delete(link);
                                 })
                             )
                         )
@@ -162,7 +162,7 @@ export class LinkManager {
         return this.relatedLinker(anchor);
     } // finds all links that contain the given anchor
     public getAllDirectLinks(anchor?: Doc): Doc[] {
-        return anchor ? Array.from(Doc.GetProto(anchor)[DirectLinks]) : [];
+        return anchor ? Array.from(anchor[DirectLinks]) : [];
     } // finds all links that contain the given anchor
 
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index e51770c25..2cc64f415 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -1,10 +1,7 @@
-import * as rp from 'request-promise';
-import { DocServer } from '../DocServer';
 import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
-import { Utils } from '../../Utils';
-import { DocumentType } from '../documents/DocumentTypes';
 import { StrCast } from '../../fields/Types';
+import { DocumentType } from '../documents/DocumentTypes';
 
 export namespace SearchUtil {
     export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
@@ -110,154 +107,4 @@ export namespace SearchUtil {
             depth++;
         }
     }
-    export interface IdSearchResult {
-        ids: string[];
-        lines: string[][];
-        numFound: number;
-        highlighting: HighlightingResult | undefined;
-    }
-
-    export interface DocSearchResult {
-        docs: Doc[];
-        lines: string[][];
-        numFound: number;
-        highlighting: HighlightingResult | undefined;
-    }
-
-    export interface SearchParams {
-        hl?: string;
-        'hl.fl'?: string;
-        start?: number;
-        rows?: number;
-        fq?: string;
-        sort?: string;
-        allowEmbeddings?: boolean;
-        onlyEmbeddings?: boolean;
-        facet?: string;
-        'facet.field'?: string;
-    }
-    export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
-    export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
-    export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
-        query = query || '*'; //If we just have a filter query, search for * as the query
-        const rpquery = Utils.prepend('/dashsearch');
-        let replacedQuery = query.replace(/type_t:([^ )])/g, (substring, arg) => `{!join from=id to=proto_i}*:* AND ${arg}`);
-        if (options.onlyEmbeddings) {
-            const header = query.match(/_[atnb]?:/) ? replacedQuery : 'DEFAULT:' + replacedQuery;
-            replacedQuery = `{!join from=id to=proto_i}* AND ${header}`;
-        }
-        //console.log("Q: " + replacedQuery + " fq: " + options.fq);
-        const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } });
-        const result: IdSearchResult = gotten.startsWith('<') ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
-        if (!returnDocs) {
-            return result;
-        }
-
-        const { ids, highlighting } = result;
-
-        const txtresult =
-            query !== '*' &&
-            JSON.parse(
-                await rp.get(Utils.prepend('/textsearch'), {
-                    qs: { ...options, q: query.replace(/^[ \+\?\*\|]*/, '') }, // a leading '+' leads to a server crash since findInFiles doesn't handle regex failures
-                })
-            );
-
-        const fileids = txtresult ? txtresult.ids : [];
-        const newIds: string[] = [];
-        const newLines: string[][] = [];
-        // bcz: we stopped storing fileUpload id's, so this won't find anything
-        // if (fileids) {
-        //     await Promise.all(
-        //         fileids.map(async (tr: string, i: number) => {
-        //             const docQuery = 'fileUpload_t:' + tr.substr(0, 7); //If we just have a filter query, search for * as the query
-        //             const docResult = JSON.parse(await rp.get(Utils.prepend('/dashsearch'), { qs: { ...options, q: docQuery } }));
-        //             newIds.push(...docResult.ids);
-        //             newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
-        //         })
-        //     );
-        // }
-
-        const theDocs: Doc[] = [];
-        const theLines: string[][] = [];
-        const textDocMap = await DocServer.GetRefFields(newIds);
-        const textDocs = newIds.map((id: string) => textDocMap[id]).map(doc => doc as Doc);
-        for (let i = 0; i < textDocs.length; i++) {
-            const testDoc = textDocs[i];
-            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
-                theDocs.push(Doc.GetProto(testDoc));
-                theLines.push(newLines[i].map(line => line.replace(query, query.toUpperCase())));
-            }
-        }
-
-        const docMap = await DocServer.GetRefFields(ids);
-        const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
-        for (let i = 0; i < ids.length; i++) {
-            const testDoc = docs[i];
-            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowEmbeddings || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
-                theDocs.push(testDoc);
-                theLines.push([]);
-            } else {
-                result.numFound--;
-            }
-        }
-
-        return { docs: theDocs, numFound: Math.max(0, result.numFound), highlighting, lines: theLines };
-    }
-
-    export async function GetEmbeddingsOfDocument(doc: Doc): Promise<Doc[]>;
-    export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs: false): Promise<string[]>;
-    export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs = true): Promise<Doc[] | string[]> {
-        const proto = Doc.GetProto(doc);
-        const protoId = proto[Id];
-        if (returnDocs) {
-            return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).docs;
-        } else {
-            return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).ids;
-        }
-        // return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
-    }
-
-    export async function GetViewsOfDocument(doc: Doc): Promise<Doc[]> {
-        const results = await Search('', true, { fq: `proto_i:"${doc[Id]}"` });
-        return results.docs;
-    }
-
-    export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[]; embeddingContexts: Doc[] }> {
-        const docContexts = (await Search('', true, { fq: `data_l:"${doc[Id]}"` })).docs;
-        const embeddings = await GetEmbeddingsOfDocument(doc, false);
-        const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', true, { fq: `data_l:"${doc}"` })));
-        const contexts = { contexts: docContexts, embeddingContexts: [] as Doc[] };
-        embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.docs));
-        return contexts;
-    }
-
-    export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[]; embeddingContexts: string[] }> {
-        const docContexts = (await Search('', false, { fq: `data_l:"${doc[Id]}"` })).ids;
-        const embeddings = await GetEmbeddingsOfDocument(doc, false);
-        const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', false, { fq: `data_l:"${doc}"` })));
-        const contexts = { contexts: docContexts, embeddingContexts: [] as string[] };
-        embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.ids));
-        return contexts;
-    }
-
-    export async function GetAllDocs() {
-        const query = '*';
-        const response = await rp.get(Utils.prepend('/dashsearch'), {
-            qs: { start: 0, rows: 10000, q: query },
-        });
-        const result: IdSearchResult = JSON.parse(response);
-        const { ids, numFound, highlighting } = result;
-        const docMap = await DocServer.GetRefFields(ids);
-        const docs: Doc[] = [];
-        for (const id of ids) {
-            const field = docMap[id];
-            if (field instanceof Doc) {
-                docs.push(field);
-            }
-        }
-        return docs;
-        // const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc);
-        // return docs as Doc[];
-    }
 }
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 0233c4051..5bf9e5b00 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -5,13 +5,13 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { BsGoogle } from 'react-icons/bs';
 import { FaFillDrip, FaPalette } from 'react-icons/fa';
+import { Utils, addStyleSheet, addStyleSheetRule } from '../../Utils';
 import { Doc, Opt } from '../../fields/Doc';
 import { DashVersion } from '../../fields/DocSymbols';
 import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
-import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
-import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Networking } from '../Network';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { GestureOverlay } from '../views/GestureOverlay';
 import { MainViewModal } from '../views/MainViewModal';
 import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
@@ -47,7 +47,6 @@ export class SettingsManager extends React.Component<{}> {
     @observable activeTab = 'Accounts';
 
     @observable public propertiesWidth: number = 0;
-    @observable public headerBarHeight: number = 0;
 
     constructor(props: {}) {
         super(props);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 523721b84..472d419fc 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -5,7 +5,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { FaPlus } from 'react-icons/fa';
 import { Doc, DocListCast } from '../../fields/Doc';
-import { AclPrivate, DocAcl } from '../../fields/DocSymbols';
+import { AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
@@ -64,7 +64,7 @@ export class DashboardView extends ObservableReactComponent<{}> {
 
     getDashboards = (whichGroup: DashboardGroup) => {
         if (whichGroup === DashboardGroup.MyDashboards) {
-            return DocListCast(Doc.MyDashboards.data).filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
+            return DocListCast(Doc.MyDashboards.data).filter(dashboard => dashboard[DocData].author === Doc.CurrentUserEmail);
         }
         return DocListCast(Doc.MySharedDocs.data_dashboards).filter(doc => doc.dockingConfig);
     };
@@ -171,7 +171,7 @@ export class DashboardView extends ObservableReactComponent<{}> {
                                               }
                                           />
                                           <div className="info">
-                                              <EditableText type={Type.PRIM} color={color} val={StrCast(dashboard.title)} setVal={val => (Doc.GetProto(dashboard).title = val)} />
+                                              <EditableText type={Type.PRIM} color={color} val={StrCast(dashboard.title)} setVal={val => (dashboard[DocData].title = val)} />
                                               {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
                                               <div
                                                   className="more"
@@ -353,7 +353,7 @@ export class DashboardView extends ObservableReactComponent<{}> {
                 },
             ],
         };
-        if (dashboard.dockingConfig && dashboard.dockingConfig !== Doc.GetProto(dashboard).dockingConfig) dashboard.dockingConfig = JSON.stringify(reset);
+        if (dashboard.dockingConfig && dashboard.dockingConfig !== dashboard[DocData].dockingConfig) dashboard.dockingConfig = JSON.stringify(reset);
         else Doc.SetInPlace(dashboard, 'dockingConfig', JSON.stringify(reset), true);
         return reset;
     };
@@ -391,9 +391,9 @@ export class DashboardView extends ObservableReactComponent<{}> {
         Doc.ActivePresentation = undefined;
     };
 
-    public static SetupDashboardCalendars(dashboardDoc: Doc){
+    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',
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 6aba4a042..0e5a4f013 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -2,22 +2,62 @@ 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 { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
 import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
-import { Transform } from '../util/Transform';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
-import { DocumentView } from './nodes/DocumentView';
+import { FieldViewProps, FocusViewOptions } from './nodes/FieldView';
+import { DocumentView, OpenWhere } from './nodes/DocumentView';
+import { PinProps } from './nodes/trails';
+import { RefField } from '../../fields/RefField';
 
 /**
- * DocComponent returns a generic React base class used by Doc views (not the 'Box' views that render the contents of Doc views)
- *    (e.g.,CollectionFreeFormDocumentView, DocumentViewInternal)
- *
+ * Shared interface among all viewBox'es (ie, react classes that render the contents of a Doc)
+ * Many of these methods only make sense for specific viewBox'es, but they should be written to
+ * be as general as possible
+ */
+export interface ViewBoxInterface {
+    fieldKey?: string;
+    annotationKey?: string;
+    updateIcon?: () => void; // updates the icon representation of the document
+    getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
+    restoreView?: (viewSpec: Doc) => boolean;
+    scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus
+    brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
+    getView?: (doc: Doc, options: FocusViewOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
+    addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
+    addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
+    select?: (ctrlKey: boolean, shiftKey: boolean) => void;
+    focus?: (textAnchor: Doc, options: FocusViewOptions) => Opt<number>;
+    isAnyChildContentActive?: () => boolean; // is any child content of the document active
+    onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected
+    getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
+    setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
+    playFrom?: (time: number, endTime?: number) => void;
+    Pause?: () => void; // pause a media document (eg, audio/video)
+    IsPlaying?: () => boolean; // is a media document playing
+    TogglePause?: (keep?: boolean) => void; // toggle media document playing state
+    setFocus?: () => void; // sets input focus to the componentView
+    setData?: (data: Field | Promise<RefField | undefined>) => boolean;
+    componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
+    dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
+    incrementalRendering?: () => void;
+    infoUI?: () => JSX.Element | null;
+    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 };
+    ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
+    snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
+    search?: (str: string, bwd?: boolean, clear?: boolean) => boolean;
+}
+/**
+ * DocComponent returns a React base class used by Doc views with accessors for unpacking he Document,layoutDoc, and dataDoc's
+ *  (note: this should not be used for the 'Box' views that render the contents of Doc views)
+ *  Example derived views: CollectionFreeFormDocumentView, DocumentView, DocumentViewInternal)
  * */
 export interface DocComponentProps {
     Document: Doc;
@@ -26,7 +66,7 @@ export interface DocComponentProps {
 }
 export function DocComponent<P extends DocComponentProps>() {
     class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
-        constructor(props: any) {
+        constructor(props: P) {
             super(props);
             makeObservable(this);
         }
@@ -41,28 +81,25 @@ export function DocComponent<P extends DocComponentProps>() {
         }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() {
-            return this._props.Document[DocData];
+            return this.Document[DocData];
         }
     }
     return Component;
 }
 
 /**
- *  ViewBoxBaseComponent  -  base class for non-annotatable views that render the interior contents of a DocumentView
- *                           (e.g. InkingStroke, ColorBox)
+ *   base class for non-annotatable views that render the interior contents of a DocumentView.
+ *   this unpacks the Document/layout/data docs as well as the fieldKey being rendered,
+ *   and provides accessors for DocumentView and ScreenToLocalBoxXf
+ *   Example views include: InkingStroke, FontIconBox, EquationBox, etc
  */
-export interface ViewBoxBaseProps {
-    Document: Doc;
-    TemplateDataDocument?: Doc;
-    DocumentView?: () => DocumentView;
-    fieldKey: string;
-    isSelected: () => boolean;
-    isContentActive: () => boolean | undefined;
-    ScreenToLocalTransform: () => Transform;
-    renderDepth: number;
-}
-export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
+export function ViewBoxBaseComponent<P extends FieldViewProps>() {
     class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
+        constructor(props: P) {
+            super(props);
+            makeObservable(this);
+        }
+
         ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform();
 
         get DocumentView() {
@@ -89,36 +126,27 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
 }
 
 /**
- * DocAnnotatableComponent - base class for annotatable views that render the interior contents of a DocumentView
- *                       (e.g., PdfBox, ImageBox)
- * These views should be interactive (respond to pointerEvents) when their conatainer DocumentView is selected
+ * base class for annotatable views that render the interior contents of a DocumentView
+ * This does what ViewBoxBaseComponent does and additionally provides accessor for the
+ * field key where annotations are stored as well as add/move/remove methods for handing
+ * annotations.
+ * This also provides methods to determine when the contents should be interactive
+ * (respond to pointerEvents) such as when the DocumentView container is selected or a
+ * peer child of the container is selected
+ * Example views include: PDFBox, ImageBox, MapBox, etc
  */
-export interface ViewBoxAnnotatableProps {
-    Document: Doc;
-    TemplateDataDocument?: Doc;
-    DocumentView?: () => DocumentView;
-    fieldKey: string;
-    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
-    isContentActive: () => boolean | undefined;
-    select: (isCtrlPressed: boolean) => void;
-    whenChildContentsActiveChanged: (isActive: boolean) => void;
-    ScreenToLocalTransform: () => Transform;
-    isSelected: () => boolean;
-    renderDepth: number;
-    isAnnotationOverlay?: boolean;
-}
-export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
+export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
     class Component extends ObservableReactComponent<React.PropsWithChildren<P>> {
-        constructor(props: any) {
+        @observable _annotationKeySuffix = () => 'annotations';
+        @observable _isAnyChildContentActive = false;
+
+        constructor(props: P) {
             super(props);
             makeObservable(this);
         }
 
         ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform();
 
-        @observable _annotationKeySuffix = () => 'annotations';
-        @observable _isAnyChildContentActive = false;
-
         get DocumentView() {
             return this._props.DocumentView;
         }
@@ -139,9 +167,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
         @computed get fieldKey() {
             return this._props.fieldKey;
         }
-
-        isAnyChildContentActive = () => this._isAnyChildContentActive;
-
         @computed public get annotationKey() {
             return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : '');
         }
@@ -162,7 +187,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 toRemove.forEach(doc => {
                     leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                     Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
-                    Doc.RemoveDocFromList(Doc.GetProto(doc), 'proto_embeddings', doc);
+                    Doc.RemoveDocFromList(doc[DocData], 'proto_embeddings', doc);
                     doc.embedContainer = undefined;
                     if (recent) {
                         doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
@@ -210,8 +235,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
                     added.forEach(doc => {
                         doc._dragOnlyWithinContainer = undefined;
-                        if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.Document;
-                        else Doc.GetProto(doc).annotationOn = undefined;
+                        if (annotationKey ?? this._annotationKeySuffix()) doc[DocData].annotationOn = this.Document;
+                        else doc[DocData].annotationOn = undefined;
                         Doc.SetContainer(doc, this.Document);
                         inheritParentAcls(targetDataDoc, doc, true);
                     });
@@ -225,6 +250,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             return true;
         };
 
+        isAnyChildContentActive = () => this._isAnyChildContentActive;
+
         whenChildContentsActiveChanged = action((isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
     }
     return Component;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 8db972ef0..5ef62b2c5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -8,7 +8,7 @@ import { FaUndo } from 'react-icons/fa';
 import { Utils, emptyFunction, lightOrDark, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit } from '../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, DocData } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
@@ -101,7 +101,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             { x: 0, y: 0, r: 0, b: 0 }
             : SelectionManager.Views
                 .filter(dv => dv._props.renderDepth > 0)
-                .map(dv => dv.getBounds())
+                .map(dv => dv.getBounds)
                 .reduce((bounds, rect) => !rect ? bounds
                             : { x: Math.min(rect.left, bounds.x),
                                 y: Math.min(rect.top, bounds.y),
@@ -203,7 +203,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         const containers = new Set<Doc | undefined>();
         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 { left, top } = dragDocView.getBounds || { left: 0, top: 0 };
         const dragData = new DragManager.DocumentDragData(
             SelectionManager.Views.map(dv => dv.Document),
             dragDocView._props.dropAction
@@ -541,17 +541,17 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             }
 
             if (['right', 'left'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeWidth(doc)) {
-                const setData = Doc.NativeWidth(Doc.GetProto(doc)) === doc.nativeWidth;
+                const setData = Doc.NativeWidth(doc[DocData]) === doc.nativeWidth;
                 doc.nativeWidth = scale.x * Doc.NativeWidth(doc);
-                if (setData) Doc.SetNativeWidth(Doc.GetProto(doc), NumCast(doc.nativeWidth));
+                if (setData) Doc.SetNativeWidth(doc[DocData], NumCast(doc.nativeWidth));
                 if (doc.layout_reflowVertical && !NumCast(doc.nativeHeight)) {
                     doc._nativeHeight = (initHeight / initWidth) * nwidth; // initializes the nativeHeight for a PDF
                 }
             }
             if (['bottom', 'top'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) {
-                const setData = Doc.NativeHeight(Doc.GetProto(doc)) === doc.nativeHeight;
+                const setData = Doc.NativeHeight(doc[DocData]) === doc.nativeHeight;
                 doc._nativeHeight = scale.y * Doc.NativeHeight(doc);
-                if (setData) Doc.SetNativeHeight(Doc.GetProto(doc), NumCast(doc._nativeHeight));
+                if (setData) Doc.SetNativeHeight(doc[DocData], NumCast(doc._nativeHeight));
             }
 
             doc._width = Math.max(1, NumCast(doc._width) * scale.x);
@@ -601,7 +601,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         this._inkDragDocs
             .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
             .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
-                Doc.GetProto(doc).data = new InkField(inkPts.map(
+                doc[DocData].data = new InkField(inkPts.map(
                     (ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth)
                         X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width,
                         Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height,
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 11b05352d..92644d3c5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -34,21 +34,19 @@ import { InteractionUtils } from '../util/InteractionUtils';
 import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
 import { ContextMenu } from './ContextMenu';
-import { ViewBoxBaseComponent, ViewBoxBaseProps } from './DocComponent';
+import { ViewBoxBaseComponent, ViewBoxInterface } from './DocComponent';
 import { Colors } from './global/globalEnums';
 import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
 import './InkStroke.scss';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { InkTangentHandles } from './InkTangentHandles';
-import { DocComponentView } from './nodes/DocumentView';
 import { FieldView, FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { PinProps, PresBox } from './nodes/trails';
 import { StyleProp } from './StyleProvider';
-import { Transform } from '../util/Transform';
 const { default: { INK_MASK_SIZE } } = require('./global/globalCssVariables.module.scss'); // prettier-ignore
 @observer
-export class InkingStroke extends ViewBoxBaseComponent<ViewBoxBaseProps & FieldViewProps>() {
+export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() implements ViewBoxInterface {
     static readonly MaskDim = INK_MASK_SIZE; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big)
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(InkingStroke, fieldStr);
@@ -64,7 +62,7 @@ export class InkingStroke extends ViewBoxBaseComponent<ViewBoxBaseProps & FieldV
     @observable _nearestScrPt?: { X: number; Y: number }; // nearst screen point on the ink stroke ""
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         this._disposers.selfDisper = reaction(
             () => this._props.isSelected(), // react to stroke being deselected by turning off ink handles
             selected => !selected && (InkStrokeProperties.Instance._controlButton = false)
@@ -332,8 +330,8 @@ export class InkingStroke extends ViewBoxBaseComponent<ViewBoxBaseProps & FieldV
         );
     };
 
-    _subContentView: DocComponentView | undefined;
-    setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
+    _subContentView: ViewBoxInterface | undefined;
+    setSubContentView = (doc: ViewBoxInterface) => (this._subContentView = doc);
     @computed get fillColor() {
         const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
         return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FillColor) ?? 'transparent';
@@ -462,7 +460,7 @@ export class InkingStroke extends ViewBoxBaseComponent<ViewBoxBaseProps & FieldV
                         <FormattedTextBox
                             {...this._props}
                             setHeight={undefined}
-                            setContentView={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
+                            setContentViewBox={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
                             yPadding={10}
                             xPadding={10}
                             fieldKey="text"
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 3b86d24a6..530fd3121 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -267,7 +267,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
                             addDocTab={this.AddDocTab}
                             pinToPres={TabDocView.PinDoc}
                             bringToFront={emptyFunction}
-                            onBrowseClick={DocumentView.exploreMode}
+                            onBrowseClickScript={DocumentView.exploreMode}
                             focus={emptyFunction}
                         />
                     </GestureOverlay>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index d700ea020..9061e98d4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -15,6 +15,7 @@ import { DocServer } from '../DocServer';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { Docs } from '../documents/Documents';
+import { CalendarManager } from '../util/CalendarManager';
 import { CaptureManager } from '../util/CaptureManager';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
@@ -31,7 +32,6 @@ import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
 import { ReportManager } from '../util/reportManager/ReportManager';
 import { ComponentDecorations } from './ComponentDecorations';
-import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp';
 import { ContextMenu } from './ContextMenu';
 import { DashboardView } from './DashboardView';
 import { DictationOverlay } from './DictationOverlay';
@@ -55,11 +55,13 @@ import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOpti
 import { CollectionLinearView } from './collections/collectionLinear';
 import { LinkMenu } from './linking/LinkMenu';
 import { AudioBox } from './nodes/AudioBox';
+import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp';
 import { DocButtonState } from './nodes/DocumentLinksButton';
 import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from './nodes/DocumentView';
 import { ImageBox } from './nodes/ImageBox';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview';
+import { DirectionsAnchorMenu } from './nodes/MapBox/DirectionsAnchorMenu';
 import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu';
 import { TaskCompletionBox } from './nodes/TaskCompletedBox';
 import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
@@ -70,9 +72,8 @@ import { PresBox } from './nodes/trails';
 import { AnchorMenu } from './pdf/AnchorMenu';
 import { GPTPopup } from './pdf/GPTPopup/GPTPopup';
 import { TopBar } from './topbar/TopBar';
+import { DocData } from '../../fields/DocSymbols';
 const { default: { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } } = require('./global/globalCssVariables.module.scss'); // prettier-ignore
-import { DirectionsAnchorMenu } from './nodes/MapBox/DirectionsAnchorMenu';
-import { CalendarManager } from '../util/CalendarManager';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -130,9 +131,19 @@ export class MainView extends ObservableReactComponent<{}> {
     @computed public get mainFreeform(): Opt<Doc> {
         return (docs => (docs?.length > 1 ? docs[1] : undefined))(DocListCast(this.mainContainer!.data));
     }
+    @observable public headerBarHeight: number = 0;
+    headerBarHeightFunc = () => this.headerBarHeight;
 
+    @action
+    toggleTopBar = () => {
+        if (this.headerBarHeight > 0) {
+            this.headerBarHeight = 0;
+        } else {
+            this.headerBarHeight = 60;
+        }
+    };
     headerBarDocWidth = () => this.mainDocViewWidth();
-    headerBarDocHeight = () => (this._hideUI ? 0 : SettingsManager.Instance?.headerBarHeight ?? 0);
+    headerBarDocHeight = () => (this._hideUI ? 0 : this.headerBarHeight ?? 0);
     topMenuHeight = () => (this._hideUI ? 0 : 35);
     topMenuWidth = returnZero; // value is ignored ...
     leftMenuWidth = () => (this._hideUI ? 0 : Number(LEFT_MENU_WIDTH.replace('px', '')));
@@ -630,9 +641,11 @@ export class MainView extends ObservableReactComponent<{}> {
         );
     }
     @computed get mainDocView() {
+        const headerBar = this._hideUI || !this.headerBarDocHeight?.() ? null : this.headerBarDocView;
+        console.log('Header = ' + this._hideUI + ' ' + this.headerBarDocHeight?.() + ' ' + headerBar);
         return (
             <>
-                {this._hideUI || !this.headerBarDocHeight?.() ? null : this.headerBarDocView}
+                {headerBar}
                 <DocumentView
                     key="main"
                     Document={this.mainContainer!}
@@ -783,7 +796,7 @@ export class MainView extends ObservableReactComponent<{}> {
 
     @action
     selectMenu = (button: Doc) => {
-        const title = StrCast(Doc.GetProto(button).title);
+        const title = StrCast(button[DocData].title);
         const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
         this.closeFlyout();
         if (willOpen) {
@@ -998,7 +1011,7 @@ export class MainView extends ObservableReactComponent<{}> {
                 <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfSidebarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
                 <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
                 {this._hideUI ? null : <TopBar />}
-                {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
+                <LinkDescriptionPopup />
                 {DocButtonState.Instance.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocButtonState.Instance.LinkEditorDocView = undefined))} docView={DocButtonState.Instance.LinkEditorDocView} /> : null}
                 {LinkInfo.Instance?.LinkInfo ? <LinkDocPreview {...LinkInfo.Instance.LinkInfo} /> : null}
 
@@ -1008,7 +1021,7 @@ export class MainView extends ObservableReactComponent<{}> {
                         default:
                         case 'dashboard': return (<>
                                         <div key="dashdiv" style={{ position: 'relative', display: this._hideUI || LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 2001 }}>
-                                            <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                                            <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} toggleTopBar={this.toggleTopBar} topBarHeight={this.headerBarHeightFunc}/>
                                         </div>
                                         {this.mainDashboardArea}
                                     </> );
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index d0516336c..a4303c3aa 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -118,7 +118,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
             })
         );
 
-        const textRegionAnnoProto = Doc.GetProto(textRegionAnno);
+        const textRegionAnnoProto = textRegionAnno[DocData];
         textRegionAnnoProto.y = Math.max(minY, 0);
         textRegionAnnoProto.x = Math.max(minX, 0);
         textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
@@ -226,9 +226,10 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
                   DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                       dragComplete: e => {
                           if (!e.aborted && e.linkDocument) {
-                              Doc.GetProto(e.linkDocument).link_relationship = 'cropped image';
-                              Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.Document.title;
-                              Doc.GetProto(e.linkDocument).link_displayLine = false;
+                              const linkDocData = e.linkDocument[DocData];
+                              linkDocData.link_relationship = 'cropped image';
+                              linkDocData.title = 'crop: ' + this.props.Document.title;
+                              linkDocData.link_displayLine = false;
                           }
                       },
                   });
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 13057ffbf..952c8f0b4 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -37,7 +37,8 @@ import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import { PropertiesSection } from './PropertiesSection';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
-import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
+import { DocumentView, OpenWhere } from './nodes/DocumentView';
+import { StyleProviderFuncType } from './nodes/FieldView';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 const _global = (window /* browser */ || global) /* node */ as any;
@@ -45,7 +46,7 @@ const _global = (window /* browser */ || global) /* node */ as any;
 interface PropertiesViewProps {
     width: number;
     height: number;
-    styleProvider?: StyleProviderFunc;
+    styleProvider?: StyleProviderFuncType;
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
 }
 
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 086f40e96..623201ed1 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -11,6 +11,7 @@ import { EditableView } from './EditableView';
 import { DocumentIconContainer } from './nodes/DocumentIcon';
 import { OverlayView } from './OverlayView';
 import './ScriptBox.scss';
+import { DocData } from '../../fields/DocSymbols';
 
 export interface ScriptBoxProps {
     onSave: (text: string, onError: (error: string) => void) => void;
@@ -102,7 +103,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
                 onCancel={overlayDisposer}
                 onSave={(text, onError) => {
                     if (!text) {
-                        Doc.GetProto(doc)[fieldKey] = undefined;
+                        doc[DocData][fieldKey] = undefined;
                     } else {
                         const script = CompileScript(text, {
                             params: { this: Doc.name, ...contextParams },
@@ -125,7 +126,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
                         div.innerHTML = 'button';
                         params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, (button: Doc) => {}, clientX, clientY);
 
-                        Doc.GetProto(doc)[fieldKey] = new ScriptField(script);
+                        doc[DocData][fieldKey] = new ScriptField(script);
                         overlayDisposer();
                     }
                 }}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 0f4a4260c..1e9272e93 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -18,6 +18,7 @@ import { StyleProp } from './StyleProvider';
 import { CollectionStackingView } from './collections/CollectionStackingView';
 import { FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { DocData } from '../../fields/DocSymbols';
 
 interface ExtraProps {
     fieldKey: string;
@@ -90,7 +91,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
             .map(data => {
                 const key = data.split(':')[0];
                 const val = Field.Copy(this.allMetadata.get(key));
-                Doc.GetProto(target)[key] = val;
+                target[DocData][key] = val;
                 return {
                     type: 'dashField',
                     attrs: { fieldKey: key, docId: '', hideKey: false, editable: true },
@@ -98,7 +99,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
                 };
             });
 
-        if (!anchor.text) Doc.GetProto(anchor).text = '-selection-';
+        if (!anchor.text) anchor[DocData].text = '-selection-';
         const textLines: any = [
             {
                 type: 'paragraph',
@@ -133,7 +134,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
         };
         if (taggedContent.length) textLines.push(metadatatext);
         if (textLines.length) {
-            Doc.GetProto(target).text = new RichTextField(
+            target[DocData].text = new RichTextField(
                 JSON.stringify({
                     doc: {
                         type: 'doc',
@@ -234,7 +235,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
                 <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
                     <CollectionStackingView
                         {...this._props}
-                        setContentView={emptyFunction}
+                        setContentViewBox={emptyFunction}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
                         ref={this._stackRef}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 1cb5ff249..2aba439c8 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -24,6 +24,7 @@ import { FieldViewProps } from './nodes/FieldView';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { PropertiesView } from './PropertiesView';
 import './StyleProvider.scss';
+import { DocumentViewProps } from './nodes/DocumentView';
 
 export enum StyleProp {
     TreeViewIcon = 'treeView_Icon',
@@ -65,13 +66,12 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) {
 
 // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
 //
-export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any {
+export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & DocumentViewProps>, property: string): any {
     const remoteDocHeader = 'author;author_date;noMargin';
     const isCaption = property.includes(':caption');
     const isAnchor = property.includes(':anchor');
     const isNonTransparent = property.includes(':nonTransparent');
     const isNonTransparentLevel = isNonTransparent ? Number(property.replace(/.*:nonTransparent([0-9]+).*/, '$1')) : 0; // property.includes(':nonTransparent');
-    const isContent = property.includes(':content');
     const isAnnotated = property.includes(':annotated');
     const isInk = () => doc?._layout_isSvg && !props?.LayoutTemplateString;
     const isOpen = property.includes(':open');
@@ -125,7 +125,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>,
         case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)));
         case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._text_fontWeight, StrCast(Doc.UserDoc().fontWeight)));
         case StyleProp.FillColor:  return StrCast(doc?._fillColor, StrCast(doc?.fillColor, 'transparent'));
-        case StyleProp.ShowCaption:return doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption);
+        case StyleProp.ShowCaption:return props?.hideCaptions || doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption);
         case StyleProp.TitleHeight:return (props?.ScreenToLocalTransform().Scale ?? 1) * NumCast(Doc.UserDoc().headerHeight,30);
         case StyleProp.ShowTitle:
             return (
@@ -244,7 +244,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>,
                         ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (props?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : lockedPosition()
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
-                        : StrCast(doc.layout_fieldKey).includes('_inline')  // if doc is an inline document in a text box
+                        : fieldKey.includes('_inline')  // if doc is an inline document in a text box
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0vw 0vw 0.1vw')}`
                         : DocCast(doc.embedContainer)?.type=== DocumentType.RTF // if doc is embedded in a text document (but not an inline)
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
@@ -316,11 +316,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>,
             };
             const audio = () => {
                 const audioAnnoState = (doc: Doc) => StrCast(doc.audioAnnoState, 'stopped');
-                const audioAnnosCount = (doc: Doc) => StrListCast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations']).length;
+                const audioAnnosCount = (doc: Doc) => StrListCast(doc[fieldKey + 'audioAnnotations']).length;
                 if (!doc || props?.renderDepth === -1 || !audioAnnosCount(doc)) return null;
                 const audioIconColors: { [key: string]: string } = { recording: 'red', playing: 'green', stopped: 'blue' };
                 return (
-                    <Tooltip title={<div>{StrListCast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations_text']).lastElement()}</div>}>
+                    <Tooltip title={<div>{StrListCast(doc[fieldKey + 'audioAnnotations_text']).lastElement()}</div>}>
                         <div className="styleProvider-audio" onPointerDown={() => DocumentManager.Instance.getFirstDocumentView(doc)?.playAnnotation()}>
                             <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors[audioAnnoState(doc)] }} icon={'file-audio'} size="sm" />
                         </div>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index e5154efcb..ba7120b93 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -11,9 +11,10 @@ import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { Transform } from '../util/Transform';
 import { undoBatch } from '../util/UndoManager';
 import { CollectionTreeView } from './collections/CollectionTreeView';
-import { DocumentView } from './nodes/DocumentView';
+import { DocumentView, returnEmptyDocViewList } from './nodes/DocumentView';
 import { DefaultStyleProvider } from './StyleProvider';
 import './TemplateMenu.scss';
+import { DocData } from '../../fields/DocSymbols';
 
 @observer
 class TemplateToggle extends React.Component<{ template: string; checked: boolean; toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
@@ -79,7 +80,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     };
     componentDidMount() {
         !this._addedKeys && (this._addedKeys = new ObservableSet());
-        [...Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].Document))), ...Array.from(Object.keys(this.props.docViews[0].Document))]
+        [...Array.from(Object.keys(this.props.docViews[0].Document[DocData])), ...Array.from(Object.keys(this.props.docViews[0].Document))]
             .filter(key => key.startsWith('layout_'))
             .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', ''))));
     }
@@ -113,6 +114,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 {templateMenu}
                 <CollectionTreeView
                     Document={Doc.MyTemplates}
+                    docViewPath={returnEmptyDocViewList}
                     styleProvider={DefaultStyleProvider}
                     childFilters={returnEmptyFilter}
                     childFiltersByRanges={returnEmptyFilter}
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx
index 4c0a917f5..cbcc980a9 100644
--- a/src/client/views/collections/CollectionCalendarView.tsx
+++ b/src/client/views/collections/CollectionCalendarView.tsx
@@ -62,7 +62,7 @@ export class CollectionCalendarView extends CollectionSubView() {
     screenToLocalTransform = () =>
         this._props
             .ScreenToLocalTransform()
-            .translate(Doc.NativeWidth(this._props.Document), 0)
+            .translate(Doc.NativeWidth(this.Document), 0)
             .scale(this._props.NativeDimScaling?.() || 1);
 
     get calendarsKey() {
@@ -74,7 +74,7 @@ export class CollectionCalendarView extends CollectionSubView() {
             <div className="collectionCalendarView">
                 <CollectionStackingView
                     {...this._props}
-                    setContentView={emptyFunction}
+                    setContentViewBox={emptyFunction}
                     ref={this._stackRef}
                     PanelHeight={this.panelHeight}
                     PanelWidth={this._props.PanelWidth}
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 8e072e235..2d2cb4860 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -10,7 +10,8 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FieldView';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView } from './CollectionSubView';
 const { default: { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
@@ -49,7 +50,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
             .translate(-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, -((Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2)
             .scale(1 / this.centerScale);
 
-    focus = (anchor: Doc, options: DocFocusOptions) => {
+    focus = (anchor: Doc, options: FocusViewOptions) => {
         const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
         if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
         options.didMove = true;
@@ -70,7 +71,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
                     NativeWidth={returnZero}
                     NativeHeight={returnZero}
                     layout_fitWidth={undefined}
-                    onDoubleClick={this.onChildDoubleClick}
+                    onDoubleClickScript={this.onChildDoubleClick}
                     renderDepth={this._props.renderDepth + 1}
                     LayoutTemplate={this._props.childLayoutTemplate}
                     LayoutTemplateString={this._props.childLayoutString}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 208fc45b5..79f5baabe 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -66,9 +66,9 @@ export class CollectionCarouselView extends CollectionSubView() {
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
                         layout_fitWidth={undefined}
-                        setContentView={undefined}
-                        onDoubleClick={this.onContentDoubleClick}
-                        onClick={this.onContentClick}
+                        setContentViewBox={undefined}
+                        onDoubleClickScript={this.onContentDoubleClick}
+                        onClickScript={this.onContentClick}
                         isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
                         isContentActive={this._props.childContentsActive ?? this._props.isContentActive() === false ? returnFalse : emptyFunction}
                         hideCaptions={!!carouselShowsCaptions} // hide captions if the carousel is configured to show the captions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1f867fc44..87973fd81 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -4,7 +4,7 @@ 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';
-import { AclAdmin, AclEdit } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
@@ -277,8 +277,8 @@ export class CollectionDockingView extends CollectionSubView() {
     }
     setupGoldenLayout = async () => {
         if (this._unmounting) return;
-        //const config = StrCast(this._props.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this._props.Document)));
-        const config = StrCast(this._props.Document.dockingConfig);
+        //const config = StrCast(this.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.Document)));
+        const config = StrCast(this.Document.dockingConfig);
         if (config) {
             const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
             const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? [];
@@ -322,7 +322,7 @@ export class CollectionDockingView extends CollectionSubView() {
             );
             new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
             this._reactionDisposer = reaction(
-                () => StrCast(this._props.Document.dockingConfig),
+                () => StrCast(this.Document.dockingConfig),
                 config => {
                     if (!this._goldenLayout || this._ignoreStateChange !== config) {
                         //  bcz: TODO! really need to diff config with ignoreStateChange and modify the current goldenLayout instead of building a new one.
@@ -381,7 +381,7 @@ export class CollectionDockingView extends CollectionSubView() {
                   .map(id => DocServer.GetCachedRefField(id))
                   .filter(f => f)
                   .map(f => f as Doc);
-        const changesMade = this._props.Document.dockingConfig !== json;
+        const changesMade = this.Document.dockingConfig !== json;
         if (changesMade) {
             if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
                 this.layoutDoc.dockingConfig = json;
@@ -449,7 +449,7 @@ export class CollectionDockingView extends CollectionSubView() {
         if (clone) {
             const cloned = await Doc.MakeClone(doc);
             Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id])));
-            Doc.GetProto(cloned.clone).dockingConfig = json;
+            cloned.clone[DocData].dockingConfig = json;
             return DashboardView.openDashboard(cloned.clone);
         }
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -463,7 +463,7 @@ export class CollectionDockingView extends CollectionSubView() {
             const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeEmbedding(origtab);
             const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc));
             if (newtabdocs.length) {
-                Doc.GetProto(newtab).data = new List<Doc>(newtabdocs);
+                newtab[DocData].data = new List<Doc>(newtabdocs);
                 newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab));
             }
             json = json.replace(origtab[Id], newtab[Id]);
@@ -479,7 +479,7 @@ export class CollectionDockingView extends CollectionSubView() {
     stateChanged = () => {
         this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         const json = JSON.stringify(this._goldenLayout.toConfig());
-        const changesMade = this._props.Document.dockingConfig !== json;
+        const changesMade = this.Document.dockingConfig !== json;
         return changesMade;
     };
 
@@ -490,7 +490,7 @@ export class CollectionDockingView extends CollectionSubView() {
             // 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.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);
+            Doc.RemoveDocFromList(tab.DashDoc[DocData], 'proto_embeddings', tab.DashDoc);
         }
         if (CollectionDockingView.Instance) {
             const dview = CollectionDockingView.Instance.Document;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index a54a5ec81..2dad377e0 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -19,14 +19,17 @@ import { undoBatch } from '../../util/UndoManager';
 import { AntimodeMenu } from '../AntimodeMenu';
 import { EditableView } from '../EditableView';
 import { MainView } from '../MainView';
-import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView';
 import { DefaultStyleProvider } from '../StyleProvider';
 import { CollectionLinearView } from './collectionLinear';
 import './CollectionMenu.scss';
+import { DocData } from '../../../fields/DocSymbols';
 
 interface CollectionMenuProps {
     panelHeight: () => number;
     panelWidth: () => number;
+    toggleTopBar: () => void;
+    topBarHeight: () => number;
 }
 
 @observer
@@ -65,15 +68,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
         }
     };
 
-    @action
-    toggleTopBar = () => {
-        if (SettingsManager.Instance.headerBarHeight > 0) {
-            SettingsManager.Instance.headerBarHeight = 0;
-        } else {
-            SettingsManager.Instance.headerBarHeight = 60;
-        }
-    };
-
     @action
     toggleProperties = () => {
         if (MainView.Instance.propertiesWidth() > 0) {
@@ -95,6 +89,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
             <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this._props.panelHeight() }}>
                 <CollectionLinearView
                     Document={selDoc}
+                    docViewPath={returnEmptyDocViewList}
                     fieldKey="data"
                     dropAction="embed"
                     styleProvider={DefaultStyleProvider}
@@ -123,8 +118,8 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
     }
 
     render() {
-        const headerIcon = SettingsManager.Instance.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down';
-        const headerTitle = SettingsManager.Instance.headerBarHeight > 0 ? 'Close Header Bar' : 'Open Header Bar';
+        const headerIcon = this.props.topBarHeight() > 0 ? 'angle-double-up' : 'angle-double-down';
+        const headerTitle = this.props.topBarHeight() > 0 ? 'Close Header Bar' : 'Open Header Bar';
         const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
         const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Properties' : 'Open Properties';
 
@@ -134,8 +129,8 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
                     toggleType={ToggleType.BUTTON}
                     type={Type.PRIM}
                     color={SettingsManager.userColor}
-                    onClick={this.toggleTopBar}
-                    toggleStatus={SettingsManager.Instance.headerBarHeight > 0}
+                    onClick={this.props.toggleTopBar}
+                    toggleStatus={this.props.topBarHeight() > 0}
                     icon={<FontAwesomeIcon icon={headerIcon} size="lg" />}
                     tooltip={headerTitle}
                 />
@@ -219,7 +214,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         params: ['target', 'source'],
         title: 'set content',
         script: 'getProto(this.target).data = copyField(this.source);',
-        immediate: undoBatch((source: Doc[]) => (Doc.GetProto(this.target).data = new List<Doc>(source))),
+        immediate: undoBatch((source: Doc[]) => (this.target[DocData].data = new List<Doc>(source))),
         initialize: emptyFunction,
     };
     _onClickCommand = {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 363db8850..e6ce5baab 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,6 +1,6 @@
-import * as React from 'react';
 import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, Field, Opt } from '../../../fields/Doc';
 import { DocData } from '../../../fields/DocSymbols';
 import { Copy, Id } from '../../../fields/FieldSymbols';
@@ -18,8 +18,8 @@ import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { LightboxView } from '../LightboxView';
-import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import './CollectionNoteTakingView.scss';
@@ -51,7 +51,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     }
 
     @computed get chromeHidden() {
-        return BoolCast(this.layoutDoc.chromeHidden) || this._props.onBrowseClick?.() ? true : false;
+        return BoolCast(this.layoutDoc.chromeHidden) || this._props.onBrowseClickScript?.() ? true : false;
     }
     // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
     @computed get colHeaderData() {
@@ -189,7 +189,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     };
 
     // let's dive in and get the actual document we want to drag/move around
-    focusDocument = (doc: Doc, options: DocFocusOptions) => {
+    focusDocument = (doc: Doc, options: FocusViewOptions) => {
         Doc.BrushDoc(doc);
         const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
         if (found) {
@@ -254,9 +254,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 rootSelected={this.rootSelected}
                 layout_showTitle={this._props.childlayout_showTitle}
                 dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType}
-                onClick={this.onChildClickHandler}
-                onBrowseClick={this._props.onBrowseClick}
-                onDoubleClick={this.onChildDoubleClickHandler}
+                onClickScript={this.onChildClickHandler}
+                onBrowseClickScript={this._props.onBrowseClickScript}
+                onDoubleClickScript={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={noteTakingDocTransform}
                 focus={this.focusDocument}
                 childFilters={this.childDocFilters}
@@ -410,7 +410,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         if ((e.ctrlKey || fieldProps.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
             e.stopPropagation?.();
             const newDoc = Doc.MakeCopy(fieldProps.Document, true);
-            Doc.GetProto(newDoc).text = undefined;
+            newDoc[DocData].text = undefined;
             FormattedTextBox.SetSelectOnLoad(newDoc);
             return this.addDocument?.(newDoc);
         }
@@ -442,7 +442,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 }
                 return true;
             }
-        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this._props.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
+        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
             const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
             if (!this._props.addDocument?.(source)) e.preventDefault();
             de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 1239a038a..1394e62ba 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -3,6 +3,7 @@ import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import { Doc, Opt } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
@@ -23,17 +24,17 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { CollectionSubView } from '../collections/CollectionSubView';
 import { LightboxView } from '../LightboxView';
 import { AudioWaveform } from '../nodes/audio/AudioWaveform';
-import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
+import { DocumentView, OpenWhere } from '../nodes/DocumentView';
+import { FocusFuncType, FocusViewOptions, StyleProviderFuncType } from '../nodes/FieldView';
 import { LabelBox } from '../nodes/LabelBox';
 import { VideoBox } from '../nodes/VideoBox';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import './CollectionStackedTimeline.scss';
-import { FieldViewProps } from '../nodes/FieldView';
 
 export type CollectionStackedTimelineProps = {
     Play: () => void;
     Pause: () => void;
-    playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
+    playLink: (linkDoc: Doc, options: FocusViewOptions) => void;
     playFrom: (seekTimeInSeconds: number, endTime?: number) => void;
     playing: () => boolean;
     thumbnails?: () => string[];
@@ -162,7 +163,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     makeDocUnfiltered = (doc: Doc) => this.childDocList?.some(item => item === doc);
 
-    getView = async (doc: Doc, options: DocFocusOptions): Promise<Opt<DocumentView>> =>
+    getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
         new Promise<Opt<DocumentView>>(res => {
             if (doc.hidden) options.didMove = !(doc.hidden = false);
             const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv));
@@ -427,8 +428,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 _isTimelineLabel: true,
                 layout_borderRounding: anchorEndTime === undefined ? '100%' : undefined,
             });
-        Doc.GetProto(anchor)[startTag] = anchorStartTime;
-        Doc.GetProto(anchor)[endTag] = anchorEndTime;
+        anchor[DocData][startTag] = anchorStartTime;
+        anchor[DocData][endTag] = anchorEndTime;
         if (addAsAnnotation) {
             if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
                 Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
@@ -579,7 +580,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                             const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
                             const width = (timespan / this.clipDuration) * this.timelineContentWidth;
                             const height = this._props.PanelHeight() / maxLevel;
-                            return this._props.Document.hideAnchors ? null : (
+                            return this.Document.hideAnchors ? null : (
                                 <div
                                     className={'collectionStackedTimeline-marker-timeline'}
                                     key={d.anchor[Id]}
@@ -691,8 +692,8 @@ interface StackedTimelineAnchorProps {
     width: number;
     height: number;
     toTimeline: (screen_delta: number, width: number) => number;
-    styleProvider?: StyleProviderFunc;
-    playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
+    styleProvider?: StyleProviderFuncType;
+    playLink: (linkDoc: Doc, options: FocusViewOptions) => void;
     setTime: (time: number) => void;
     startTag: string;
     endTag: string;
@@ -701,7 +702,7 @@ interface StackedTimelineAnchorProps {
     isDocumentActive?: () => boolean | undefined;
     ScreenToLocalTransform: () => Transform;
     _timeline: HTMLDivElement | null;
-    focus: DocFocusFunc;
+    focus: FocusFuncType;
     currentTimecode: () => number;
     isSelected: () => boolean;
     stackedTimeline: CollectionStackedTimeline;
@@ -810,7 +811,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
     // renders anchor LabelBox
     renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
         const anchor = observable({ view: undefined as Opt<DocumentView> | null });
-        const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => {
+        const focusFunc = (doc: Doc, options: FocusViewOptions): number | undefined => {
             this._props.playLink(mark, options);
             return undefined;
         };
@@ -842,8 +843,8 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
                     searchFilterDocs={returnEmptyDoclist}
                     childFilters={returnEmptyFilter}
                     childFiltersByRanges={returnEmptyFilter}
-                    onClick={script}
-                    onDoubleClick={this._props.layoutDoc.autoPlayAnchors ? undefined : doublescript}
+                    onClickScript={script}
+                    onDoubleClickScript={this._props.layoutDoc.autoPlayAnchors ? undefined : doublescript}
                     ignoreAutoHeight={false}
                     hideResizeHandles={true}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 9384b7088..0da32a9e0 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -24,8 +24,8 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { LightboxView } from '../LightboxView';
 import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
-import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
@@ -203,7 +203,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
 
     componentDidMount() {
         super.componentDidMount?.();
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
 
         // reset section headers when a new filter is inputted
         this._pivotFieldDisposer = reaction(
@@ -249,7 +249,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     };
 
     // let's dive in and get the actual document we want to drag/move around
-    focusDocument = (doc: Doc, options: DocFocusOptions) => {
+    focusDocument = (doc: Doc, options: FocusViewOptions) => {
         Doc.BrushDoc(doc);
 
         const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
@@ -288,7 +288,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) {
                 newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey];
             }
-            Doc.GetProto(newDoc).text = undefined;
+            newDoc[DocData].text = undefined;
             FormattedTextBox.SetSelectOnLoad(newDoc);
             return this.addDocument?.(newDoc);
         }
@@ -324,13 +324,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 renderDepth={this._props.renderDepth + 1}
                 PanelWidth={panelWidth}
                 PanelHeight={panelHeight}
-                pointerEvents={this.DocumentView?.()._props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
+                pointerEvents={this.DocumentView?.()._props.onClickScript?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
                 styleProvider={this.styleProvider}
                 containerViewPath={this.childContainerViewPath}
                 layout_fitWidth={this.childFitWidth}
                 isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
-                onBrowseClick={this._props.onBrowseClick}
+                onBrowseClickScript={this._props.onBrowseClickScript}
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this._props.childLayoutTemplate}
                 LayoutTemplateString={this._props.childLayoutString}
@@ -341,8 +341,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 rootSelected={this.rootSelected}
                 layout_showTitle={this._props.childlayout_showTitle}
                 dragAction={(this.layoutDoc.childDragAction ?? this._props.childDragAction) as dropActionType}
-                onClick={this.onChildClickHandler}
-                onDoubleClick={this.onChildDoubleClickHandler}
+                onClickScript={this.onChildClickHandler}
+                onDoubleClickScript={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={stackedDocTransform}
                 focus={this.focusDocument}
                 childFilters={this.childDocFilters}
@@ -468,7 +468,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 }
                 return true;
             }
-        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this._props.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
+        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
             const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
             if (!this._props.addDocument?.(source)) e.preventDefault();
             de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
@@ -551,7 +551,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
                 colHeaderData={this.colHeaderData}
-                Document={this._props.Document}
+                Document={this.Document}
                 TemplateDataDocument={this._props.TemplateDataDocument}
                 renderChildren={this.children}
                 columnWidth={this.columnWidth}
@@ -583,7 +583,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return (
             <CollectionMasonryViewFieldRow
                 showHandle={first}
-                Document={this._props.Document}
+                Document={this.Document}
                 chromeHidden={this.chromeHidden}
                 pivotField={this.pivotField}
                 unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
@@ -670,7 +670,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     addDocument={this._props.addDocument}
                     moveDocument={this._props.moveDocument}
                     addDocTab={this._props.addDocTab}
-                    onBrowseClick={this._props.onBrowseClick}
+                    onBrowseClickScript={this._props.onBrowseClickScript}
                     pinToPres={emptyFunction}
                     rootSelected={this.rootSelected}
                     removeDocument={this._props.removeDocument}
@@ -738,7 +738,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         onWheel={e => this.isContentActive() && e.stopPropagation()}>
                         {this.renderedSections}
                         {!this.showAddAGroup ? null : (
-                            <div key={`${this._props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" style={{ width: !this.isStackingView ? '100%' : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+                            <div key={`${this.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" style={{ width: !this.isStackingView ? '100%' : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
                                 <EditableView {...editableViewProps} />
                             </div>
                         )}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d7c42a975..0aa74aaba 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -21,18 +21,17 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { UndoManager, undoBatch } from '../../util/UndoManager';
-import { ViewBoxBaseComponent, ViewBoxBaseProps } from '../DocComponent';
+import { ViewBoxBaseComponent } from '../DocComponent';
 import { LoadingBox } from '../nodes/LoadingBox';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { CollectionView, CollectionViewProps } from './CollectionView';
-import { returnEmptyDocViewList } from '../nodes/DocumentView';
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     isAnyChildContentActive: () => boolean;
 }
 
 export function CollectionSubView<X>(moreProps?: X) {
-    class CollectionSubView extends ViewBoxBaseComponent<X & SubCollectionViewProps & ViewBoxBaseProps>() {
+    class CollectionSubView extends ViewBoxBaseComponent<X & SubCollectionViewProps>() {
         private dropDisposer?: DragManager.DragDropDisposer;
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected _mainCont?: HTMLDivElement;
@@ -63,20 +62,16 @@ export function CollectionSubView<X>(moreProps?: X) {
         }
 
         get dataDoc() {
-            return this._props.TemplateDataDocument instanceof Doc && this._props.Document.isTemplateForField
-                ? Doc.GetProto(this._props.TemplateDataDocument)
-                : this._props.Document.resolvedDataDoc
-                  ? this._props.Document
-                  : Doc.GetProto(this._props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
+            return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField ? Doc.GetProto(this._props.TemplateDataDocument) : this.Document.resolvedDataDoc ? this.Document : Doc.GetProto(this.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
         }
 
         get childContainerViewPath() {
-            return this.DocumentView?.().docViewPath ?? returnEmptyDocViewList;
+            return this.DocumentView?.().docViewPath;
         }
         // this returns whether either the collection is selected, or the template that it is part of is selected
         rootSelected = () => this._props.isSelected() || BoolCast(this._props.TemplateDataDocument && 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.TemplateDataDocument.
+        // The data field for rendering this collection will be on the this.Document unless we're rendering a template in which case we try to use props.TemplateDataDocument.
         // When a document has a TemplateDataDoc but it's not a template, then it contains its own rendering data, but needs to pass the TemplateDataDoc through
         // to its children which may be templates.
         // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey'
@@ -97,14 +92,14 @@ export function CollectionSubView<X>(moreProps?: X) {
         @computed get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
-        collectionFilters = () => this._focusFilters ?? StrListCast(this._props.Document._childFilters);
-        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this._props.Document._childFiltersByRanges, listSpec('string'), []);
+        collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
+        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
         // child filters apply to the descendants of the documents in this collection
         childDocFilters = () => [...(this._props.childFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
         // unrecursive filters apply to the documents in the collection, but no their children.  See Utils.noRecursionHack
         unrecursiveDocFilters = () => [...(this._props.childFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
         childDocRangeFilters = () => [...(this._props.childFiltersByRanges?.() || []), ...this.collectionRangeDocFilters()];
-        searchFilterDocs = () => this._props.searchFilterDocs?.() ?? DocListCast(this._props.Document._searchFilterDocs);
+        searchFilterDocs = () => this._props.searchFilterDocs?.() ?? DocListCast(this.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
             let rawdocs: (Doc | Promise<Doc>)[] = [];
@@ -127,7 +122,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             const childDocFilters = this.childDocFilters();
             const childFiltersByRanges = this.childDocRangeFilters();
             const searchDocs = this.searchFilterDocs();
-            if (this._props.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !childFiltersByRanges.length && !searchDocs.length)) {
+            if (this.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !childFiltersByRanges.length && !searchDocs.length)) {
                 return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
             }
 
@@ -136,9 +131,9 @@ export function CollectionSubView<X>(moreProps?: X) {
                 // dragging facets
                 const dragged = this._props.childFilters?.().some(f => f.includes(Utils.noDragDocsFilter));
                 if (dragged && SnappingManager.CanEmbed && DragManager.docsBeingDragged.includes(d)) return false;
-                let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this._props.Document).length > 0;
+                let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.Document).length > 0;
                 if (notFiltered) {
-                    notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this._props.Document).length > 0;
+                    notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.Document).length > 0;
                     const fieldKey = Doc.LayoutFieldKey(d);
                     const annos = !Field.toString(Doc.LayoutField(d) as Field).includes(CollectionView.name);
                     const data = d[annos ? fieldKey + '_annotations' : fieldKey];
@@ -171,7 +166,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         @action
         protected async setCursorPosition(position: [number, number]) {
             let ind;
-            const doc = this._props.Document;
+            const doc = this.Document;
             const id = Doc.UserDoc()[Id];
             const email = Doc.CurrentUserEmail;
             const pos = { x: position[0], y: position[1] };
@@ -207,7 +202,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 const dropAction = this.layoutDoc.dropAction as dropActionType;
                 // if the dropEvent's dragAction is, say 'embed', but we're just dragging within a collection, we may not actually want to make an embedding.
                 // so we check if our collection has a dropAction set on it and if so, we use that instead.
-                if (dropAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this._props.Document && this.childDocs.includes(d))) {
+                if (dropAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.Document && this.childDocs.includes(d))) {
                     de.complete.docDragData.dropAction = dropAction;
                 }
                 e.stopPropagation();
@@ -471,7 +466,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             }
             if (generatedDocuments.length) {
                 // Creating a dash document
-                const isFreeformView = this._props.Document._type_collection === CollectionViewType.Freeform;
+                const isFreeformView = this.Document._type_collection === CollectionViewType.Freeform;
                 const set = !isFreeformView
                     ? generatedDocuments
                     : generatedDocuments.length > 1
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 7036ec41c..ee5147428 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -16,7 +16,8 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
-import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FieldView';
 import { PresBox } from '../nodes/trails';
 import { CollectionSubView } from './CollectionSubView';
 import './CollectionTimeView.scss';
@@ -38,7 +39,7 @@ export class CollectionTimeView extends CollectionSubView() {
     }
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript('openInLightbox(this)', { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
@@ -68,7 +69,7 @@ export class CollectionTimeView extends CollectionSubView() {
     };
 
     @action
-    scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => {
+    scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: FocusViewOptions) => {
         // if in preview, then override document's fields with view spec
         this._focusFilters = StrListCast(anchor.config_docFilters);
         this._focusRangeFilters = StrListCast(anchor.config_docRangeFilters);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 76b934802..4696d7948 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -447,7 +447,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 {!(this.Document instanceof Doc) || !this.treeChildren ? null : this.Document.treeView_HasOverlay ? (
                     <CollectionFreeFormView
                         {...this._props}
-                        setContentView={emptyFunction}
+                        setContentViewBox={emptyFunction}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
                         pointerEvents={this._props.isContentActive() && SnappingManager.IsDragging ? returnAll : returnNone}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7c9ef9f48..26ff5cc06 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,7 +13,7 @@ import { dropActionType } from '../../util/DragManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
 import { OpenWhere } from '../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { CollectionCalendarView } from './CollectionCalendarView';
@@ -33,7 +33,7 @@ import { CollectionLinearView } from './collectionLinear';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
 import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
-interface CollectionViewProps_ extends FieldViewProps {
+export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
     isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
     layoutEngine?: () => string;
@@ -63,9 +63,8 @@ interface CollectionViewProps_ extends FieldViewProps {
     RemFromMap?: (treeViewDoc: Doc, index: number[]) => void;
     hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views)
 }
-export interface CollectionViewProps extends React.PropsWithChildren<CollectionViewProps_> {}
 @observer
-export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() {
+export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(CollectionView, fieldStr);
     }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f00e42360..da15f8dc5 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -29,7 +29,8 @@ import { LightboxView } from '../LightboxView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { Colors } from '../global/globalEnums';
-import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from '../nodes/DocumentView';
+import { DocumentView, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from '../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
 import { KeyValueBox } from '../nodes/KeyValueBox';
 import { DashFieldView } from '../nodes/formattedText/DashFieldView';
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
@@ -37,7 +38,6 @@ import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { FieldViewProps } from '../nodes/FieldView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -118,7 +118,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
             titleEle.onchange = (e: any) => {
                 undoable(() => {
                     titleEle.size = e.currentTarget.value.length + 3;
-                    Doc.GetProto(doc).title = e.currentTarget.value;
+                    doc[DocData].title = e.currentTarget.value;
                 }, 'edit tab title')();
             };
 
@@ -416,7 +416,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
         return tab !== undefined;
     };
     @action
-    focusFunc = (doc: Doc, options: DocFocusOptions) => {
+    focusFunc = (doc: Doc, options: FocusViewOptions) => {
         if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
             this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
         }
@@ -452,7 +452,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
                     hideTitle={this._props.keyValue}
                     Document={this._document}
                     TemplateDataDocument={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined}
-                    onBrowseClick={DocumentView.exploreMode}
+                    onBrowseClickScript={DocumentView.exploreMode}
                     waitForDoubleClickToClick={this.waitForDoubleClick}
                     isContentActive={this.isContentActive}
                     isDocumentActive={returnFalse}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 9f40edee1..38a2fe978 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -27,8 +27,8 @@ import { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
 import { EditableView } from '../EditableView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { StyleProp } from '../StyleProvider';
-import { DocumentView, DocumentViewInternal, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
+import { FieldViewProps, StyleProviderFuncType } from '../nodes/FieldView';
 import { KeyValueBox } from '../nodes/KeyValueBox';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
@@ -62,7 +62,7 @@ export interface TreeViewProps {
     ScreenToLocalTransform: () => Transform;
     contextMenuItems?: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
     dontRegisterView?: boolean;
-    styleProvider?: StyleProviderFunc | undefined;
+    styleProvider?: StyleProviderFuncType | undefined;
     treeViewHideHeaderFields: () => boolean;
     renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
     onCheckedClick?: () => ScriptField;
@@ -367,8 +367,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
             _width: 1000,
             _height: 10,
         });
-        Doc.GetProto(bullet).title = ComputedField.MakeFunction('this.text?.Text');
-        Doc.GetProto(bullet).data = new List<Doc>([]);
+        const bulletData = bullet[DocData];
+        bulletData.title = ComputedField.MakeFunction('this.text?.Text');
+        bulletData.data = new List<Doc>([]);
         DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
 
         return bullet;
@@ -982,8 +983,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                 addDocument={undefined}
                 addDocTab={this._props.addDocTab}
                 pinToPres={this.treeView._props.pinToPres}
-                onClick={this.onChildClick}
-                onDoubleClick={this.onChildDoubleClick}
+                onClickScript={this.onChildClick}
+                onDoubleClickScript={this.onChildDoubleClick}
                 dragAction={this._props.dragAction}
                 moveDocument={this.move}
                 removeDocument={this._props.removeDoc}
@@ -1076,7 +1077,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
                     fitContentsToBox={returnTrue}
                     hideDecorationTitle={this.treeView.outlineMode}
                     hideResizeHandles={this.treeView.outlineMode}
-                    onClick={this.onChildClick}
+                    onClickScript={this.onChildClick}
                     focus={this.refocus}
                     onKey={this.onKeyDown}
                     hideLinkButton={BoolCast(this.treeView.Document.childHideLinkButton)}
@@ -1209,7 +1210,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         move: DragManager.MoveFunction,
         dragAction: dropActionType,
         addDocTab: (doc: Doc, where: OpenWhere) => boolean,
-        styleProvider: undefined | StyleProviderFunc,
+        styleProvider: undefined | StyleProviderFuncType,
         screenToLocalXf: () => Transform,
         isContentActive: (outsideReaction?: boolean) => boolean,
         panelWidth: () => number,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 5204633ea..f0a31a8c6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -130,7 +130,7 @@ export class CollectionFreeFormLinkView extends ObservableReactComponent<Collect
                 LinkManager.currentLink = this._props.LinkDocs[0];
                 this.toggleProperties();
                 // OverlayView.Instance.addElement(
-                //     <LinkEditor sourceDoc={this._props.A._props.Document} linkDoc={this._props.LinkDocs[0]}
+                //     <LinkEditor sourceDoc={this._props.A.Document} linkDoc={this._props.LinkDocs[0]}
                 //         showLinks={action(() => { })}
                 //     />, { x: 300, y: 300 });
             })
@@ -204,8 +204,8 @@ export class CollectionFreeFormLinkView extends ObservableReactComponent<Collect
         const atop = this.visibleY(adiv);
         const btop = this.visibleY(bdiv);
         if (!a.width || !b.width) return undefined;
-        const aDocBounds = (A._props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
-        const bDocBounds = (B._props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
+        const aDocBounds = (A._props as any).DocumentView?.().getBounds || { left: 0, right: 0, top: 0, bottom: 0 };
+        const bDocBounds = (B._props as any).DocumentView?.().getBounds || { left: 0, right: 0, top: 0, bottom: 0 };
         const aleft = this.visibleX(adiv);
         const bleft = this.visibleX(bdiv);
         const aclipped = aleft !== a.left || atop !== a.top;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c4358747b..7203041e0 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, reaction, runInAction, toJS } 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';
@@ -38,8 +38,8 @@ import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } fr
 import { LightboxView } from '../../LightboxView';
 import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewWrapper } from '../../nodes/CollectionFreeFormDocumentView';
 import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
-import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
+import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../../nodes/FieldView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../../nodes/trails/PresBox';
 import { CreateImage } from '../../nodes/WebBoxRenderer';
@@ -54,7 +54,7 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
 import './CollectionFreeFormView.scss';
 import { MarqueeView } from './MarqueeView';
 
-export type collectionFreeformViewProps = {
+export interface collectionFreeformViewProps {
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     originTopLeft?: boolean;
@@ -65,12 +65,12 @@ export type collectionFreeformViewProps = {
     noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
     engineProps?: any;
     getScrollHeight?: () => number | undefined;
-};
+}
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
     public get displayName() {
-        return 'CollectionFreeFormView(' + this._props.Document.title?.toString() + ')';
+        return 'CollectionFreeFormView(' + this.Document.title?.toString() + ')';
     } // this makes mobx trace() statements more descriptive
 
     constructor(props: any) {
@@ -180,12 +180,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
-        if (timer) clearTimeout(timer);
-        return DocumentView.SetViewTransition(docs, 'all', duration, undefined, true);
+        return DocumentView.SetViewTransition(docs, 'all', duration, timer, undefined, true);
     }
     public static updateKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], time: number) {
-        if (timer) clearTimeout(timer);
-        const newTimer = DocumentView.SetViewTransition(docs, 'all', 1000, undefined, true);
+        const newTimer = DocumentView.SetViewTransition(docs, 'all', 1000, timer, undefined, true);
         const timecode = Math.round(time);
         docs.forEach(doc => {
             CollectionFreeFormDocumentView.animFields.forEach(val => {
@@ -223,7 +221,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable _keyframeEditing = false;
     @action setKeyFrameEditing = (set: boolean) => (this._keyframeEditing = set);
     getKeyFrameEditing = () => this._keyframeEditing;
-    onBrowseClickHandler = () => this._props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick);
+    onBrowseClickHandler = () => this._props.onBrowseClickScript?.() || ScriptCast(this.layoutDoc.onBrowseClick);
     onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
     elementFunc = () => this._layoutElements;
@@ -288,14 +286,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return dispTime === -1 || curTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
     }
 
-    groupFocus = (anchor: Doc, options: DocFocusOptions) => {
+    groupFocus = (anchor: Doc, options: FocusViewOptions) => {
         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) => {
+    focus = (anchor: Doc, options: FocusViewOptions) => {
         if (this._lightboxDoc) return;
         if (anchor === this.Document) {
             if (options.willZoomCentered && options.zoomScale) {
@@ -321,7 +319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     };
 
-    getView = async (doc: Doc, options: DocFocusOptions): Promise<Opt<DocumentView>> =>
+    getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
         new Promise<Opt<DocumentView>>(res => {
             if (doc.hidden && this._lightboxDoc !== doc) options.didMove = !(doc.hidden = false);
             const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv));
@@ -467,7 +465,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.childLayoutPairs
             .map(pair => pair.layout)
             .reduce((cluster, cd) => {
-                const grouping = this._props.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
+                const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
                 if (grouping !== -1) {
                     const layoutDoc = Doc.Layout(cd);
                     const cx = NumCast(cd.x) - this._clusterDistance / 2;
@@ -484,9 +482,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (cluster !== -1) {
             const ptsParent = e;
             if (ptsParent) {
-                const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this._props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
+                const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
                 const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView?.())!);
-                const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
+                const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 };
                 const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined);
                 de.moveDocument = this._props.moveDocument;
                 de.offset = this.screenToFreeformContentsXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
@@ -506,7 +504,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     updateClusters(_freeform_useClusters: boolean) {
-        this._props.Document._freeform_useClusters = _freeform_useClusters;
+        this.Document._freeform_useClusters = _freeform_useClusters;
         this._clusterSets.length = 0;
         this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
     }
@@ -514,7 +512,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     updateClusterDocs(docs: Doc[]) {
         const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
-        if (this._props.Document._freeform_useClusters) {
+        if (this.Document._freeform_useClusters) {
             const docFirst = docs[0];
             docs.map(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
             const preferredInd = NumCast(docFirst.layout_cluster);
@@ -557,7 +555,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     updateCluster = (doc: Doc) => {
         const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
-        if (this._props.Document._freeform_useClusters) {
+        if (this.Document._freeform_useClusters) {
             this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
             const preferredInd = NumCast(doc.layout_cluster);
             doc.layout_cluster = -1;
@@ -616,7 +614,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     trySelectCluster = (addToSel: boolean) => {
         if (this._hitCluster !== -1) {
             !addToSel && SelectionManager.DeselectAll();
-            const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this._props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+            const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
             this.selectDocuments(eles);
             return true;
         }
@@ -810,7 +808,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.childDocs
             .map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.()))
             .filter(inkView => inkView?.ComponentView instanceof InkingStroke)
-            .map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
+            .map(inkView => ({ inkViewBounds: inkView!.getBounds, inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
             .filter(
                 ({ inkViewBounds }) =>
                     inkViewBounds && // bounding box of eraser segment and ink stroke overlap
@@ -959,8 +957,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const localTransform = invTransform.scaleAbout(deltaScale, x, y);
         if (localTransform.Scale >= 0.05 || localTransform.Scale > this.zoomScaling()) {
             const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20);
-            this._props.Document[this.scaleFieldKey] = Math.abs(safeScale);
-            this.setPan(-localTransform.TranslateX / safeScale, (this._props.originTopLeft ? undefined : NumCast(this._props.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
+            this.Document[this.scaleFieldKey] = Math.abs(safeScale);
+            this.setPan(-localTransform.TranslateX / safeScale, (this._props.originTopLeft ? undefined : NumCast(this.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
         }
     };
 
@@ -968,7 +966,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerWheel = (e: React.WheelEvent): void => {
         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;
+        if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return;
         e.stopPropagation();
         const docHeight = NumCast(this.Document[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;
@@ -989,7 +987,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case freeformScrollMode.Zoom:
                 if ((e.ctrlKey || !scrollable) && this._props.isContentActive()) {
                     this.zoom(e.clientX, e.clientY, Math.max(-1, Math.min(1, e.deltaY))); // if (!this._props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
-                    e.preventDefault();
+                    // e.preventDefault();
                 }
                 break;
         }
@@ -1182,13 +1180,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) {
                 newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey];
             }
-            Doc.GetProto(newDoc).text = undefined;
+            newDoc[DocData].text = undefined;
             FormattedTextBox.SetSelectOnLoad(newDoc);
             return this.addDocument?.(newDoc);
         }
     };
     @computed get childPointerEvents() {
-        const engine = this._props.layoutEngine?.() || StrCast(this._props.Document._layoutEngine);
+        const engine = this._props.layoutEngine?.() || StrCast(this.Document._layoutEngine);
         return SnappingManager.IsResizing
             ? 'none'
             : this._props.childPointerEvents?.() ??
@@ -1210,6 +1208,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 {...OmitKeys(entry, ['replica', 'pair']).omit}
                 key={childLayout[Id] + (entry.replica || '')}
                 Document={childLayout}
+                containerViewPath={this.DocumentView?.().docViewPath}
+                styleProvider={this.clusterStyleProvider}
                 TemplateDataDocument={childData}
                 dragStarting={this.dragStarting}
                 dragEnding={this.dragEnding}
@@ -1223,10 +1223,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 LayoutTemplateString={childLayout.z ? undefined : this._props.childLayoutString}
                 rootSelected={childData ? this.rootSelected : returnFalse}
                 waitForDoubleClickToClick={this._props.waitForDoubleClickToClick}
-                onClick={this.onChildClickHandler}
+                onClickScript={this.onChildClickHandler}
                 onKey={this.onKeyDown}
-                onDoubleClick={this.onChildDoubleClickHandler}
-                onBrowseClick={this.onBrowseClickHandler}
+                onDoubleClickScript={this.onChildDoubleClickHandler}
+                onBrowseClickScript={this.onBrowseClickHandler}
                 ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
                 PanelWidth={childLayout[Width]}
                 PanelHeight={childLayout[Height]}
@@ -1242,8 +1242,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 moveDocument={this._props.moveDocument}
                 pinToPres={this._props.pinToPres}
                 whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-                containerViewPath={this.DocumentView?.().docViewPath}
-                styleProvider={this.clusterStyleProvider}
                 dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
                 bringToFront={this.bringToFront}
                 layout_showTitle={this._props.childlayout_showTitle}
@@ -1322,7 +1320,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
-        (this._props.viewDefDivClick || ScriptCast(this._props.Document.onViewDefDivClick))?.script.run({ this: this._props.Document, payload });
+        (this._props.viewDefDivClick || ScriptCast(this.Document.onViewDefDivClick))?.script.run({ this: this.Document, payload });
         e.stopPropagation();
     };
 
@@ -1377,7 +1375,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         poolData: Map<string, PoolData>,
         engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[]
     ) {
-        return engine(poolData, this._props.Document, this.childLayoutPairs, [this._props.PanelWidth(), this._props.PanelHeight()], this.viewDefsToJSX, this._props.engineProps);
+        return engine(poolData, this.Document, this.childLayoutPairs, [this._props.PanelWidth(), this._props.PanelHeight()], this.viewDefsToJSX, this._props.engineProps);
     }
 
     doFreeformLayout(poolData: Map<string, PoolData>) {
@@ -1437,7 +1435,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     infoUI = () => (this.Document._hideInfo || this.Document.annotationOn || this._props.renderDepth ? null : <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} close={this.closeInfo} />);
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         super.componentDidMount?.();
         setTimeout(
             action(() => {
@@ -1639,15 +1637,15 @@ 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.Document.isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
+        !this.Document.isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
         !Doc.noviceMode &&
             appearanceItems.push({
                 description: 'Toggle auto arrange',
                 event: () => (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange),
                 icon: 'compress-arrows-alt',
             });
-        if (this._props.setContentView === emptyFunction) {
+        if (this._props.setContentViewBox === emptyFunction) {
             !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
             return;
         }
@@ -1656,7 +1654,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         !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.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;
@@ -1680,8 +1678,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @undoBatch
     transcribeStrokes = () => {
-        if (this._props.Document.isGroup && this._props.Document.transcription) {
-            const text = StrCast(this._props.Document.transcription);
+        if (this.Document.isGroup && this.Document.transcription) {
+            const text = StrCast(this.Document.transcription);
             const lines = text.split('\n');
             const height = 30 + 15 * lines.length;
 
@@ -1742,7 +1740,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @computed get placeholder() {
         return (
             <div className="collectionfreeformview-placeholder" style={{ background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) }}>
-                <span className="collectionfreeformview-placeholderSpan">{this._props.Document.annotationOn ? '' : this._props.Document.title?.toString()}</span>
+                <span className="collectionfreeformview-placeholderSpan">{this.Document.annotationOn ? '' : this.Document.title?.toString()}</span>
             </div>
         );
     }
@@ -1882,10 +1880,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                             PanelHeight={this.lightboxPanelHeight}
                             NativeWidth={returnZero}
                             NativeHeight={returnZero}
-                            onClick={this.onChildClickHandler}
+                            onClickScript={this.onChildClickHandler}
                             onKey={this.onKeyDown}
-                            onDoubleClick={this.onChildDoubleClickHandler}
-                            onBrowseClick={this.onBrowseClickHandler}
+                            onDoubleClickScript={this.onChildDoubleClickHandler}
+                            onBrowseClickScript={this.onBrowseClickHandler}
                             childFilters={this.childDocFilters}
                             childFiltersByRanges={this.childDocRangeFilters}
                             searchFilterDocs={this.searchFilterDocs}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 39d828302..c2f8232c6 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -352,9 +352,10 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
         const newCollection = creator
             ? 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';
+                  const docData = doc[DocData];
+                  docData.data = new List<Doc>(selected);
+                  docData.isGroup = makeGroup;
+                  docData.title = makeGroup ? 'grouping' : 'nested freeform';
                   doc._freeform_panX = doc._freeform_panY = 0;
                   return doc;
               })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 113ffedb3..f25872c2b 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -192,7 +192,7 @@ export class CollectionGridView extends CollectionSubView() {
                 {...this._props}
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
-                setContentView={emptyFunction}
+                setContentViewBox={emptyFunction}
                 Document={layout}
                 TemplateDataDocument={layout.resolvedDataDoc as Doc}
                 isContentActive={this.isChildContentActive}
@@ -200,7 +200,7 @@ export class CollectionGridView extends CollectionSubView() {
                 PanelHeight={height}
                 ScreenToLocalTransform={dxf}
                 whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-                onClick={this.onChildClickHandler}
+                onClickScript={this.onChildClickHandler}
                 renderDepth={this._props.renderDepth + 1}
                 dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
             />
@@ -340,7 +340,7 @@ export class CollectionGridView extends CollectionSubView() {
      * Handles text document creation on double click.
      */
     onPointerDown = (e: React.PointerEvent) => {
-        if (this._props.isContentActive(true)) {
+        if (this._props.isContentActive()) {
             setupMoveUpEvents(
                 this,
                 e,
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 40b4151fe..1fa106b45 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -267,8 +267,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
                 dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
-                onClick={this.onChildClickHandler}
-                onDoubleClick={this.onChildDoubleClickHandler}
+                onClickScript={this.onChildClickHandler}
+                onDoubleClickScript={this.onChildDoubleClickHandler}
                 suppressSetHeight={true}
                 ScreenToLocalTransform={dxf}
                 isContentActive={this.isChildContentActive}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 04a4042f1..97a444b8c 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -262,8 +262,8 @@ export class CollectionMultirowView extends CollectionSubView() {
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
                 dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
-                onClick={this.onChildClickHandler}
-                onDoubleClick={this.onChildDoubleClickHandler}
+                onClickScript={this.onChildClickHandler}
+                onDoubleClickScript={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={dxf}
                 isContentActive={this.isChildContentActive}
                 isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index c38c6dc4e..d580d9c52 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,16 +1,16 @@
-import { action, observable } from 'mobx';
+import { action } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { UndoManager } from '../../../util/UndoManager';
 import { StyleProp } from '../../StyleProvider';
-import { StyleProviderFunc } from '../../nodes/DocumentView';
+import { StyleProviderFuncType } from '../../nodes/FieldView';
 import { DimUnit } from './CollectionMulticolumnView';
 
 interface ResizerProps {
     width: number;
-    styleProvider?: StyleProviderFunc;
+    styleProvider?: StyleProviderFuncType;
     isContentActive?: () => boolean | undefined;
     columnUnitLength(): number | undefined;
     toLeft?: Doc;
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 6f1b3b425..73d08d5ef 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,16 +1,16 @@
-import { action, observable } from 'mobx';
+import { action } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { UndoManager } from '../../../util/UndoManager';
 import { StyleProp } from '../../StyleProvider';
-import { StyleProviderFunc } from '../../nodes/DocumentView';
+import { StyleProviderFuncType } from '../../nodes/FieldView';
 import { DimUnit } from './CollectionMultirowView';
 
 interface ResizerProps {
     height: number;
-    styleProvider?: StyleProviderFunc;
+    styleProvider?: StyleProviderFuncType;
     isContentActive?: () => boolean | undefined;
     columnUnitLength(): number | undefined;
     toTop?: Doc;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 227274a53..ec91b25f8 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -16,7 +16,8 @@ import { undoable, undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
-import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../../nodes/FieldView';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
@@ -24,7 +25,6 @@ import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
-import { FieldViewProps } from '../../nodes/FieldView';
 
 export enum ColumnType {
     Number,
@@ -147,7 +147,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         document.addEventListener('keydown', this.onKeyDown);
 
         Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
@@ -498,13 +498,13 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
-    focusDocument = (doc: Doc, options: DocFocusOptions) => {
+    focusDocument = (doc: Doc, options: FocusViewOptions) => {
         Doc.BrushDoc(doc);
         this.scrollToDoc(doc, options);
         return undefined;
     };
 
-    scrollToDoc = (doc: Doc, options: DocFocusOptions) => {
+    scrollToDoc = (doc: Doc, options: FocusViewOptions) => {
         const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
         if (found) {
             const rect = found.getBoundingClientRect();
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 3f94e7fc1..f2fe0dde7 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -12,7 +12,7 @@ import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoable } from '../../../util/UndoManager';
-import { ViewBoxBaseComponent, ViewBoxBaseProps } from '../../DocComponent';
+import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
@@ -20,17 +20,17 @@ import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
 
-interface SchemaRowBoxProps {
+interface SchemaRowBoxProps extends FieldViewProps {
     rowIndex: number;
 }
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRowBoxProps & ViewBoxBaseProps>() {
+export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
     public static LayoutString(fieldKey: string, rowIndex: number) {
         return FieldView.LayoutString(SchemaRowBox, fieldKey).replace('fieldKey', `rowIndex={${rowIndex}} fieldKey`);
     }
     private _ref: HTMLDivElement | null = null;
 
-    constructor(props: any) {
+    constructor(props: SchemaRowBoxProps) {
         super(props);
         makeObservable(this);
     }
@@ -50,7 +50,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo
     }
 
     componentDidMount(): void {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
 
     select = (ctrlKey: boolean, shiftKey: boolean) => {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 422c4155d..fda5764dd 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -18,7 +18,7 @@ import { EditableView } from '../../EditableView';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { DefaultStyleProvider } from '../../StyleProvider';
 import { Colors } from '../../global/globalEnums';
-import { OpenWhere } from '../../nodes/DocumentView';
+import { OpenWhere, returnEmptyDocViewList } from '../../nodes/DocumentView';
 import { FieldViewProps } from '../../nodes/FieldView';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
@@ -49,7 +49,7 @@ export interface SchemaTableCellProps {
 
 @observer
 export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> {
-    constructor(props: any) {
+    constructor(props: SchemaTableCellProps) {
         super(props);
         makeObservable(this);
     }
@@ -75,6 +75,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
         const fieldProps: FieldViewProps = {
             childFilters: returnEmptyFilter,
             childFiltersByRanges: returnEmptyFilter,
+            docViewPath: returnEmptyDocViewList,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
             isSelected: returnFalse,
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index b7376e901..12b83414c 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -61,7 +61,7 @@ export class LinkMenu extends ObservableReactComponent<Props> {
     render() {
         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());
+        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);
 
         return (
             <div className="linkMenu" ref={this._linkMenuRef} style={{ ...style, background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }}>
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 91142b90b..028d3da53 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -47,18 +47,18 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
             const sourceDoc =
                 this.props.docView.anchorViewDoc ??
                 (this.props.docView.Document.type === DocumentType.LINK //
-                    ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
+                    ? 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.Document.type === DocumentType.LINK
-                ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
-                    ? DocCast(linkDoc.link_anchor_2)
-                    : DocCast(linkDoc.link_anchor_1)
-                : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) ||
-                  LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null));
+                  ? this.props.docView._props.LayoutTemplateString?.includes('link_anchor_1')
+                      ? DocCast(linkDoc.link_anchor_2)
+                      : DocCast(linkDoc.link_anchor_1)
+                  : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) ||
+                    LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null));
             return !destDoc || !sourceDoc ? null : (
                 <LinkMenuItem
                     key={linkDoc[Id]}
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index 6760a0b1e..ed575c329 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -312,7 +312,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
                         addDocTab={this.addDocTab}
                         pinToPres={TabDocView.PinDoc}
                         bringToFront={emptyFunction}
-                        onBrowseClick={DocumentView.exploreMode}
+                        onBrowseClickScript={DocumentView.exploreMode}
                         focus={emptyFunction}
                     />
                 </GestureOverlay>
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1729af191..2b71fd156 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -5,6 +5,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DateField } from '../../../fields/DateField';
 import { Doc } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
 import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, DateCast, NumCast } from '../../../fields/Types';
 import { AudioField, nullAudio } from '../../../fields/URLField';
@@ -19,8 +20,7 @@ import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import './AudioBox.scss';
-import { DocFocusOptions } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
+import { FocusViewOptions, FieldView, FieldViewProps } from './FieldView';
 import { PinProps, PresBox } from './trails';
 
 /**
@@ -121,7 +121,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
 
     @action
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         if (this.path) {
             this.mediaState = media_state.Paused;
             this.setPlayheadTime(NumCast(this.layoutDoc.clipStart));
@@ -374,9 +374,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
             action(() => {
                 const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.Document.x), NumCast(this.Document.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(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
-                Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction(`this.${textField}_recordingSource.mediaState`);
+                const newDocData = newDoc[DocData];
+                newDocData[`${textField}_recordingSource`] = this.dataDoc;
+                newDocData[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
+                newDocData.mediaState = ComputedField.MakeFunction(`this.${textField}_recordingSource.mediaState`);
                 if (Doc.IsInMyOverlay(this.Document)) {
                     newDoc.overlayX = this.Document.x;
                     newDoc.overlayY = NumCast(this.Document.y) + NumCast(this.layoutDoc._height);
@@ -432,7 +433,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     // plays link
-    playLink = (link: Doc, options: DocFocusOptions) => {
+    playLink = (link: Doc, options: FocusViewOptions) => {
         if (link.annotationOn === this.Document) {
             if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
                 this.playFrom(this.timeline?.anchorStart(link) || 0, this.timeline?.anchorEnd(link));
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 73709c17e..0ae4ed62c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -38,8 +38,8 @@ export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentView
     CollectionFreeFormView: CollectionFreeFormView;
 }
 @observer
-export class CollectionFreeFormDocumentViewWrapper extends ObservableReactComponent<CollectionFreeFormDocumentViewWrapperProps> implements CollectionFreeFormDocumentViewProps {
-    constructor(props: any) {
+export class CollectionFreeFormDocumentViewWrapper extends ObservableReactComponent<CollectionFreeFormDocumentViewWrapperProps> {
+    constructor(props: CollectionFreeFormDocumentViewWrapperProps) {
         super(props);
         makeObservable(this);
     }
@@ -104,7 +104,7 @@ export class CollectionFreeFormDocumentViewWrapper extends ObservableReactCompon
         );
     }
 }
-export interface CollectionFreeFormDocumentViewProps {
+export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     w_X: () => number;
     w_Y: () => number;
     w_Z: () => number;
@@ -125,8 +125,8 @@ export interface CollectionFreeFormDocumentViewProps {
 }
 
 @observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps>() {
-    constructor(props: any) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
+    constructor(props: CollectionFreeFormDocumentViewProps) {
         super(props);
         makeObservable(this);
     }
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 500eb52ac..116dc48a6 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -8,20 +8,20 @@ import { DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { DocUtils, Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import './ComparisonBox.scss';
-import { DocumentView, returnEmptyDocViewList } from './DocumentView';
+import { DocumentView } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { PinProps, PresBox } from './trails';
 
 @observer
-export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(ComparisonBox, fieldKey);
     }
     private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -35,7 +35,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         return '_' + this._props.fieldKey + '_clipWidth';
     }
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
     protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
         this._disposers[disposerId]?.();
@@ -152,9 +152,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         if (property === StyleProp.PointerEvents) return 'none';
         return this._props.styleProvider?.(doc, props, property);
     };
-    @computed get childContainerViewPath() {
-        return this.DocumentView?.().docViewPath ?? returnEmptyDocViewList;
-    }
     moveDoc1 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true);
     moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true);
     remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true);
@@ -181,7 +178,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                         {...this._props}
                         Document={targetDoc}
                         TemplateDataDocument={undefined}
-                        containerViewPath={this.childContainerViewPath}
+                        containerViewPath={this.DocumentView?.().docViewPath}
                         moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2}
                         removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2}
                         NativeWidth={returnZero}
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 5ceec98cb..8365f4770 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -14,13 +14,13 @@ import { TraceMobx } from '../../../../fields/util';
 import { Docs } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { UndoManager, undoable } from '../../../util/UndoManager';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent';
 import { MarqueeAnnotator } from '../../MarqueeAnnotator';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup';
-import { DocFocusOptions, DocumentView } from '../DocumentView';
-import { FieldView, FieldViewProps } from '../FieldView';
+import { DocumentView } from '../DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
 import { PinProps } from '../trails';
 import './DataVizBox.scss';
 import { Histogram } from './components/Histogram';
@@ -36,7 +36,7 @@ export enum DataVizView {
 }
 
 @observer
-export class DataVizBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _marqueeref = React.createRef<MarqueeAnnotator>();
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -46,10 +46,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatablePr
     @observable _marqueeing: number[] | undefined = undefined;
     @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
 
-    constructor(props: ViewBoxAnnotatableProps & FieldViewProps) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
 
     @computed get annotationLayer() {
@@ -233,7 +233,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatablePr
             () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar')
         );
     };
-    getView = async (doc: Doc, options: DocFocusOptions) => {
+    getView = async (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             options.didMove = true;
             this.toggleSidebar();
@@ -254,7 +254,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatablePr
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeDocument(doc, sidebarKey);
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData();
     }
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 55859b92b..9538cebb2 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,7 +24,6 @@ import { YoutubeBox } from './../../apis/youtube/YoutubeBox';
 import { AudioBox } from './AudioBox';
 import { ComparisonBox } from './ComparisonBox';
 import { DataVizBox } from './DataVizBox/DataVizBox';
-import { DocumentViewProps } from './DocumentView';
 import './DocumentView.scss';
 import { EquationBox } from './EquationBox';
 import { FieldView, FieldViewProps } from './FieldView';
@@ -114,13 +113,11 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
     }
 }
 
+export interface DocumentContentsViewProps extends FieldViewProps {
+    layoutFieldKey: string;
+}
 @observer
-export class DocumentContentsView extends ObservableReactComponent<
-    FieldViewProps & {
-        onClick?: () => ScriptField;
-        LayoutTemplate?: () => Opt<Doc>;
-    }
-> {
+export class DocumentContentsView extends ObservableReactComponent<DocumentContentsViewProps> {
     constructor(props: any) {
         super(props);
         makeObservable(this);
@@ -130,8 +127,8 @@ export class DocumentContentsView extends ObservableReactComponent<
         TraceMobx();
         if (this._props.LayoutTemplateString) return this._props.LayoutTemplateString;
         if (!this.layoutDoc) return '<p>awaiting layout</p>';
-        if (this._props.fieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, KeyValueBox.LayoutString());
-        const layout = Cast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.fieldKey ? this._props.fieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string');
+        if (this._props.layoutFieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, KeyValueBox.LayoutString());
+        const layout = Cast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.layoutFieldKey ? this._props.layoutFieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string');
         if (layout === undefined) return this._props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString();
         if (typeof layout === 'string') return layout;
         return '<p>Loading layout</p>';
@@ -143,26 +140,26 @@ export class DocumentContentsView extends ObservableReactComponent<
         const template: Doc =
             this._props.LayoutTemplate?.() ||
             (this._props.LayoutTemplateString && this._props.Document) ||
-            (this._props.fieldKey && StrCast(this._props.Document[this._props.fieldKey]) && this._props.Document) ||
-            Doc.Layout(this._props.Document, this._props.fieldKey ? Cast(this._props.Document[this._props.fieldKey], Doc, null) : undefined);
+            (this._props.layoutFieldKey && StrCast(this._props.Document[this._props.layoutFieldKey]) && this._props.Document) ||
+            Doc.Layout(this._props.Document, this._props.layoutFieldKey ? Cast(this._props.Document[this._props.layoutFieldKey], Doc, null) : undefined);
         return Doc.expandTemplateLayout(template, this._props.Document);
     }
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
         const docOnlyProps = [
-            // these are the properties in  DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
+            // these are the properties in DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
             'hideResizeHandles',
             'hideTitle',
             'contentPointerEvents',
-            'radialMenu',
             'LayoutTemplateString',
             'LayoutTemplate',
+            'layoutFieldKey',
             'dontCenter',
             'contextMenuItems',
             //'onClick', // don't need to omit this since it will be set
-            'onDoubleClick',
-            'onPointerDown',
-            'onPointerUp',
+            'onDoubleClickScript',
+            'onPointerDownScript',
+            'onPointerUpScript',
         ];
         const templateDataDoc = this._props.TemplateDataDocument ?? (this.layoutDoc !== this._props.Document ? this._props.Document[DocData] : undefined);
         const list: BindingProps & React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLDivElement>, HTMLDivElement> = {
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 5ce08cdc3..4a22766cc 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -29,7 +29,7 @@ export class DocumentIcon extends ObservableReactComponent<DocumentIconProps> {
     }
     render() {
         const view = this._props.view;
-        const { left, top, right, bottom } = view.getBounds() || { left: 0, top: 0, right: 0, bottom: 0 };
+        const { left, top, right, bottom } = view.getBounds || { left: 0, top: 0, right: 0, bottom: 0 };
 
         return (
             <div
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index f5949a451..d1805308d 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -17,6 +17,7 @@ import { DocumentView } from './DocumentView';
 import { LinkDescriptionPopup } from './LinkDescriptionPopup';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import { PinProps } from './trails';
+import { DocData } from '../../../fields/DocSymbols';
 
 interface DocumentLinksButtonProps {
     View: DocumentView;
@@ -158,41 +159,40 @@ export class DocumentLinksButton extends ObservableReactComponent<DocumentLinksB
 
                 LinkManager.currentLink = linkDoc;
 
-                if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
-                    // if linking from a Hypothes.is annotation
-                    Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
-                    Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
-                    Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
-                    const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
-                    Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc
-                }
-
                 if (linkDoc) {
+                    if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
+                        // if linking from a Hypothes.is annotation
+                        const linkDocData = linkDoc[DocData];
+                        linkDocData.linksToAnnotation = true;
+                        linkDocData.annotationId = DocumentLinksButton.AnnotationId;
+                        linkDocData.annotationUri = DocumentLinksButton.AnnotationUri;
+                        const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
+                        Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc
+                    }
+
                     TaskCompletionBox.textDisplayed = 'Link Created';
                     TaskCompletionBox.popupX = screenX;
                     TaskCompletionBox.popupY = screenY - 133;
                     TaskCompletionBox.taskCompleted = true;
 
-                    if (LinkDescriptionPopup.showDescriptions === 'ON' || !LinkDescriptionPopup.showDescriptions) {
-                        LinkDescriptionPopup.popupX = screenX;
-                        LinkDescriptionPopup.popupY = screenY - 100;
-                        LinkDescriptionPopup.descriptionPopup = true;
+                    if (LinkDescriptionPopup.Instance.showDescriptions === 'ON' || !LinkDescriptionPopup.Instance.showDescriptions) {
+                        LinkDescriptionPopup.Instance.popupX = screenX;
+                        LinkDescriptionPopup.Instance.popupY = screenY - 100;
+                        LinkDescriptionPopup.Instance.display = true;
                     }
 
                     const rect = document.body.getBoundingClientRect();
-                    if (LinkDescriptionPopup.popupX + 200 > rect.width) {
-                        LinkDescriptionPopup.popupX -= 190;
+                    if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) {
+                        LinkDescriptionPopup.Instance.popupX -= 190;
                         TaskCompletionBox.popupX -= 40;
                     }
-                    if (LinkDescriptionPopup.popupY + 100 > rect.height) {
-                        LinkDescriptionPopup.popupY -= 40;
+                    if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) {
+                        LinkDescriptionPopup.Instance.popupY -= 40;
                         TaskCompletionBox.popupY -= 40;
                     }
 
                     setTimeout(
-                        action(() => {
-                            TaskCompletionBox.taskCompleted = false;
-                        }),
+                        action(() => (TaskCompletionBox.taskCompleted = false)),
                         2500
                     );
                 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 444c300f3..3e5e43b47 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -3,19 +3,17 @@ import { Dropdown, DropdownType, Type } from 'browndash-components';
 import { Howl } from 'howler';
 import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import { Bounce, Fade, Flip, JackInTheBox, Roll, Rotate, Zoom } from 'react-awesome-reveal';
 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 { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
-import { RefField } from '../../../fields/RefField';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { DocServer } from '../../DocServer';
@@ -33,34 +31,27 @@ import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
-import { Transform } from '../../util/Transform';
-import { UndoManager, undoBatch } from '../../util/UndoManager';
+import { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocComponent } from '../DocComponent';
+import { DocComponent, ViewBoxInterface } from '../DocComponent';
 import { EditableView } from '../EditableView';
 import { GestureOverlay } from '../GestureOverlay';
 import { LightboxView } from '../LightboxView';
-import { ObservableReactComponent } from '../ObservableReactComponent';
 import { StyleProp } from '../StyleProvider';
-import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
 import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView';
 import { DocumentLinksButton } from './DocumentLinksButton';
 import './DocumentView.scss';
-import { FieldViewProps } from './FieldView';
+import { FieldViewProps, FieldViewSharedProps } from './FieldView';
 import { KeyValueBox } from './KeyValueBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { PresEffect, PresEffectDirection } from './trails';
-import { PinProps, PresBox } from './trails/PresBox';
-
 interface Window {
     MediaRecorder: MediaRecorder;
 }
-
 declare class MediaRecorder {
-    // whatever MediaRecorder has
-    constructor(e: any);
+    constructor(e: any); // whatever MediaRecorder has
 }
 
 export enum OpenWhereMod {
@@ -92,114 +83,7 @@ export enum OpenWhere {
 export function returnEmptyDocViewList() {
     return [] as DocumentView[];
 }
-export interface DocFocusOptions {
-    willPan?: boolean; // determines whether to pan to target document
-    willZoomCentered?: boolean; // determines whether to zoom in on target document.  if zoomScale is 0, this just centers the document
-    zoomScale?: number; // percent of containing frame to zoom into document
-    zoomTime?: number;
-    didMove?: boolean; // whether a document was changed during the showDocument process
-    docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom.  when this happens the docTransform must accumulate all the transforms of each level of the hierarchy
-    instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom)
-    preview?: boolean; // whether changes should be previewed by the componentView or written to the document
-    effect?: Doc; // animation effect for focus
-    noSelect?: boolean; // whether target should be selected after focusing
-    playAudio?: boolean; // whether to play audio annotation on focus
-    playMedia?: boolean; // whether to play start target videos
-    openLocation?: OpenWhere; // where to open a missing document
-    zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
-    toggleTarget?: boolean; // whether to toggle target on and off
-    anchorDoc?: Doc; // doc containing anchor info to apply at end of focus to target doc
-    easeFunc?: 'linear' | 'ease'; // transition method for scrolling
-}
-export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => Opt<number>;
-export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => any;
-export interface DocComponentView {
-    fieldKey?: string;
-    annotationKey?: string;
-    updateIcon?: () => void; // updates the icon representation of the document
-    getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
-    restoreView?: (viewSpec: Doc) => boolean;
-    scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
-    brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
-    getView?: (doc: Doc, options: DocFocusOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
-    addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
-    addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
-    select?: (ctrlKey: boolean, shiftKey: boolean) => void;
-    focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt<number>;
-    isAnyChildContentActive?: () => boolean; // is any child content of the document active
-    onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected
-    getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
-    setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
-    playFrom?: (time: number, endTime?: number) => void;
-    Pause?: () => void; // pause a media document (eg, audio/video)
-    IsPlaying?: () => boolean; // is a media document playing
-    TogglePause?: (keep?: boolean) => void; // toggle media document playing state
-    setFocus?: () => void; // sets input focus to the componentView
-    setData?: (data: Field | Promise<RefField | undefined>) => boolean;
-    componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
-    dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
-    incrementalRendering?: () => void;
-    infoUI?: () => JSX.Element | null;
-    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 };
-    ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
-    snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
-    search?: (str: string, bwd?: boolean, clear?: boolean) => boolean;
-}
-/**
- * props that DocumentViews, DocumentInternalViews and FieldViews all can use
- * */
-export interface DocumentViewSharedProps {
-    Document: Doc;
-    LayoutTemplateString?: string;
-    TemplateDataDocument?: Doc;
-    renderDepth: number;
-    scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
-    treeViewDoc?: Doc;
-    xPadding?: number;
-    yPadding?: number;
-    dontRegisterView?: boolean;
-    dropAction?: dropActionType;
-    dragAction?: dropActionType;
-    forceAutoHeight?: boolean;
-    ignoreAutoHeight?: boolean;
-    disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
-    CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
-    containerViewPath?: () => DocumentView[];
-    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
-    isGroupActive?: () => string | undefined; // is this document part of a group that is active
-    setContentView?: (view: DocComponentView) => any;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
-    isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
-    childFilters: () => string[];
-    childFiltersByRanges: () => string[];
-    styleProvider: Opt<StyleProviderFunc>;
-    setTitleFocus?: () => void;
-    focus: DocFocusFunc;
-    layout_fitWidth?: (doc: Doc) => boolean | undefined;
-    searchFilterDocs: () => Doc[];
-    layout_showTitle?: () => string;
-    whenChildContentsActiveChanged: (isActive: boolean) => void;
-    rootSelected?: () => boolean; // whether the root of a template has been selected
-    addDocTab: (doc: Doc, where: OpenWhere) => boolean;
-    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
-    addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean;
-    removeDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean;
-    moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => boolean;
-    pinToPres: (document: Doc, pinProps: PinProps) => void;
-    ScreenToLocalTransform: () => Transform;
-    bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
-    defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
-    pointerEvents?: () => Opt<string>;
-}
-
-/**
- * props that are used by DocumentViews and DocumentInternalViews but not by the contents of those views (FieldViews).
- */
-export interface DocumentViewProps extends DocumentViewSharedProps {
+export interface DocumentViewProps extends FieldViewSharedProps {
     hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected
     hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected
     hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
@@ -221,35 +105,22 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
     NativeWidth?: () => number;
     NativeHeight?: () => number;
-    LayoutTemplate?: () => Opt<Doc>;
     contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[];
-    onClick?: () => ScriptField;
-    onDoubleClick?: () => ScriptField;
-    onPointerDown?: () => ScriptField;
-    onPointerUp?: () => ScriptField;
-    onBrowseClick?: () => ScriptField | undefined;
-    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
     dragStarting?: () => void;
     dragEnding?: () => void;
 }
-
-/**
- * props used by DocInternalViews and FieldViews but not DocumentViews
- * these props correspond to things that the DocumentView creates and thus doesn't need to receive as a prop
- */
-export interface DocumentViewInternalSharedProps {
-    select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
-    isSelected: () => boolean;
-    docViewPath: () => DocumentView[];
-    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
-}
-export interface DocumentViewInternalProps extends DocumentViewProps, DocumentViewInternalSharedProps {
-    docViewPublic: () => DocumentView;
-}
-
 @observer
-export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() {
+export class DocumentViewInternal extends DocComponent<FieldViewProps & DocumentViewProps>() {
+    // this makes mobx trace() statements more descriptive
+    public get displayName()  { return 'DocumentViewInternal(' + this.Document.title + ')'; } // prettier-ignore
     public static SelectAfterContextMenu = true; //  whether a document should be selected after it's contextmenu is triggered.
+
+    /**
+     * This function is filled in by MainView to allow non-viewBox views to add Docs as tabs without
+     * needing to know about/reference MainView
+     */
+    public static addDocTabFunc: (doc: Doc, location: OpenWhere) => boolean = returnFalse;
+
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _doubleClickTimeout: NodeJS.Timeout | undefined;
     private _singleClickFunc: undefined | (() => any);
@@ -262,79 +133,51 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     private _mainCont = React.createRef<HTMLDivElement>();
     private _titleRef = React.createRef<EditableView>();
     private _dropDisposer?: DragManager.DragDropDisposer;
-    constructor(props: any) {
+    constructor(props: FieldViewProps & DocumentViewProps) {
         super(props);
         makeObservable(this);
     }
 
-    @observable _componentView: Opt<DocComponentView> = undefined; // needs to be accessed from DocumentView wrapper class
+    @observable _changingTitleField = false;
+    @observable _titleDropDownInnerWidth = 0; // width of menu dropdown when setting doc title
+    @observable _mounted = false; // turn off all pointer events if component isn't yet mounted (enables nested Docs in alternate UI textboxes that appear on hover which otherwise would grab focus from the text box, reverting to the original UI )
+    @observable _isContentActive: boolean | undefined = undefined;
+    @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined = undefined;
+    @observable _componentView: Opt<ViewBoxInterface> = undefined; // needs to be accessed from DocumentView wrapper class
     @observable _animateScaleTime: Opt<number> = undefined; // milliseconds for animating between views.  defaults to 300 if not uset
     @observable _animateScalingTo = 0;
 
-    public get animateScaleTime() {
-        return this._animateScaleTime ?? 100;
-    }
-    public get displayName() {
-        return 'DocumentViewInternal(' + this.Document.title + ')';
-    } // this makes mobx trace() statements more descriptive
-
-    public get DocumentView() {
-        return this._props.docViewPublic;
-    }
+    get _contentDiv()  { return this._mainCont.current; } // prettier-ignore
+    get _docView()     { return this._props.DocumentView?.(); } // prettier-ignore
+
+    animateScaleTime = () => this._animateScaleTime ?? 100;
+    style = (doc: Doc, sprop: StyleProp | string) => this._props.styleProvider?.(doc, this._props, sprop);
+    @computed get layout_showTitle()   { return this.style(this.layoutDoc, StyleProp.ShowTitle) as Opt<string>; } // prettier-ignore
+    @computed get opacity()            { return this.style(this.layoutDoc, StyleProp.Opacity); } // prettier-ignore
+    @computed get boxShadow()          { return this.style(this.layoutDoc, StyleProp.BoxShadow); } // prettier-ignore
+    @computed get borderRounding()     { return this.style(this.layoutDoc, StyleProp.BorderRounding); } // prettier-ignore
+    @computed get widgetDecorations()  { return this.style(this.layoutDoc, StyleProp.Decorations); } // prettier-ignore
+    @computed get backgroundBoxColor() { return this.style(this.layoutDoc, StyleProp.BackgroundColor + ':box'); } // prettier-ignore
+    @computed get headerMargin()       { return this.style(this.layoutDoc, StyleProp.HeaderMargin) ?? 0; } // prettier-ignore
+    @computed get layout_showCaption() { return this.style(this.layoutDoc, StyleProp.ShowCaption) ?? 0;  } // prettier-ignore
+    @computed get titleHeight()        { return this.style(this.layoutDoc, StyleProp.TitleHeight) ?? 0;  } // prettier-ignore
+    @computed get docContents()        { return this.style(this.Document, StyleProp.DocContents); } // prettier-ignore
+    @computed get highlighting()       { return this.style(this.Document, StyleProp.Highlighting); } // prettier-ignore
+    @computed get borderPath()         { return this.style(this.Document, StyleProp.BorderPath); } // prettier-ignore
 
-    public get ContentDiv() {
-        return this._mainCont.current;
-    }
-    public get LayoutFieldKey() {
-        return Doc.LayoutFieldKey(this.layoutDoc);
-    }
-    @computed get styleProps(): FieldViewProps {
-        return { ...this._props, fieldKey: '' };
-    }
-    @computed get layout_showTitle() {
-        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.ShowTitle) as Opt<string>;
-    }
-    @computed get NativeDimScaling() {
-        return this._props.NativeDimScaling?.() || 1;
-    }
-    @computed get thumb() {
-        return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
-    }
-    @computed get opacity() {
-        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.Opacity);
-    }
-    @computed get boxShadow() {
-        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.BoxShadow);
-    }
-    @computed get borderRounding() {
-        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.BorderRounding);
-    }
-    @computed get widgetDecorations() {
-        TraceMobx();
-        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.Decorations);
-    }
-    @computed get backgroundBoxColor() {
-        return this._props.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.BackgroundColor + ':box');
-    }
-    @computed get docContents() {
-        return this._props.styleProvider?.(this.Document, this.styleProps, StyleProp.DocContents);
-    }
-    @computed get headerMargin() {
-        return this._props?.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.HeaderMargin) || 0;
-    }
-    @computed get layout_showCaption() {
-        return this._props?.hideCaptions ? undefined : this._props?.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.ShowCaption) || 0;
+    @computed get onClickHandler() {
+        return this._props.onClickScript?.() ?? this._props.onBrowseClickScript?.() ?? ScriptCast(this.Document.onClick, ScriptCast(this.layoutDoc.onClick));
     }
-    @computed get titleHeight() {
-        return this._props?.styleProvider?.(this.layoutDoc, this.styleProps, StyleProp.TitleHeight) || 0;
+    @computed get onDoubleClickHandler() {
+        return this._props.onDoubleClickScript?.() ?? ScriptCast(this.layoutDoc.onDoubleClick, ScriptCast(this.Document.onDoubleClick));
     }
-    @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined = undefined;
-    @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
-        return this._pointerEvents;
+    @computed get onPointerDownHandler() {
+        return this._props.onPointerDownScript?.() ?? ScriptCast(this.layoutDoc.onPointerDown, ScriptCast(this.Document.onPointerDown));
     }
-    @computed get finalLayoutKey() {
-        return StrCast(this.Document.layout_fieldKey, 'layout');
+    @computed get onPointerUpHandler() {
+        return this._props.onPointerUpScript?.() ?? ScriptCast(this.layoutDoc.onPointerUp, ScriptCast(this.Document.onPointerUp));
     }
+
     @computed get disableClickScriptFunc() {
         const onScriptDisable = this._props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable;
         // prettier-ignore
@@ -344,23 +187,50 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             (onScriptDisable !== 'never' && (this.rootSelected() || this._componentView?.isAnyChildContentActive?.()))
         );
     }
-    @computed get onClickHandler() {
-        return this._props.onClick?.() ?? this._props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
+    @computed get _rootSelected() {
+        return this._props.isSelected() || BoolCast(this._props.TemplateDataDocument && this._props.rootSelected?.());
     }
-    @computed get onDoubleClickHandler() {
-        return this._props.onDoubleClick?.() ?? Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick;
+    /// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
+    @computed get _contentPointerEvents() {
+        TraceMobx();
+        return this._props.contentPointerEvents ??
+            ((!this.disableClickScriptFunc && //
+                this.onClickHandler &&
+                !this._props.onBrowseClickScript?.() &&
+                this.isContentActive() !== true) ||
+                this.isContentActive() === false)
+            ? 'none'
+            : this._pointerEvents;
     }
-    @computed get onPointerDownHandler() {
-        return this._props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown);
+
+    // We need to use allrelatedLinks to get not just links to the document as a whole, but links to
+    // anchors that are not rendered as DocumentViews (marked as 'layout_unrendered' with their 'annotationOn' set to this document).  e.g.,
+    //     - PDF text regions are rendered as an Annotations without generating a DocumentView, '
+    //     - RTF selections are rendered via Prosemirror and have a mark which contains the Document ID for the annotation link
+    //     - and links to PDF/Web docs at a certain scroll location never create an explicit view.
+    // For each of these, we create LinkAnchorBox's on the border of the DocumentView.
+    @computed get directLinks() {
+        TraceMobx();
+        return LinkManager.Instance.getAllRelatedLinks(this.Document).filter(
+            link =>
+                (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 onPointerUpHandler() {
-        return this._props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp);
+    @computed get _allLinks() {
+        TraceMobx();
+        return LinkManager.Instance.getAllRelatedLinks(this.Document).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document);
+    }
+
+    @computed get filteredLinks() {
+        return DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines);
     }
 
     componentWillUnmount() {
         this.cleanupHandlers(true);
     }
-    @observable _mounted = false; // turn off all pointer events if component isn't yet mounted (enables nested Docs in alternate UI textboxes that appear on hover which otherwise would grab focus from the text box, reverting to the original UI )
 
     componentDidMount() {
         runInAction(() => (this._mounted = true));
@@ -380,7 +250,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             { fireImmediately: true }
         );
         this._disposers.pointerevents = reaction(
-            () => this._props.styleProvider?.(this.Document, this.styleProps, StyleProp.PointerEvents),
+            () => this.style(this.Document, StyleProp.PointerEvents),
             pointerevents => (this._pointerEvents = pointerevents),
             { fireImmediately: true }
         );
@@ -406,18 +276,19 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
 
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
-        if (this._mainCont.current) {
+        const docView = this._docView;
+        if (this._mainCont.current && docView) {
             const views = SelectionManager.Views.filter(dv => dv.ContentDiv);
-            const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [this.DocumentView()];
+            const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [docView];
             const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.Document));
-            const screenXf = this.DocumentView().screenToViewTransform();
+            const screenXf = docView.screenToViewTransform();
             const [left, top] = screenXf.inverse().transformPoint(0, 0);
             dragData.offset = screenXf.transformDirection(x - left, y - top);
             dragData.dropAction = dropAction;
             dragData.treeViewDoc = this._props.treeViewDoc;
             dragData.removeDocument = this._props.removeDocument;
             dragData.moveDocument = this._props.moveDocument;
-            dragData.draggedViews = [this.DocumentView()];
+            dragData.draggedViews = [docView];
             dragData.canEmbed = this.Document.dragAction ?? this._props.dragAction ? true : false;
             DragManager.StartDocumentDrag(
                 selected.map(dv => dv.ContentDiv!),
@@ -434,12 +305,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!StrCast(this.layoutDoc._layout_showTitle)) this.layoutDoc._layout_showTitle = 'title';
         setTimeout(() => this._titleRef.current?.setIsFocused(true)); // use timeout in case title wasn't shown to allow re-render so that titleref will be defined
     };
-
-    public static addDocTabFunc: (doc: Doc, location: OpenWhere) => boolean = returnFalse;
-
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
         if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return;
-        if (!this.Document.ignoreClick && this._props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
+        const documentView = this._docView;
+        if (documentView && !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.layoutDoc._keepZWhenDragged && this._props.bringToFront(this.Document);
@@ -451,7 +320,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     const func = () => this.onDoubleClickHandler.script.run( {
                             this: this.Document,
                             scriptContext: this._props.scriptContext,
-                            documentView: this.DocumentView(),
+                            documentView,
                             clientX, clientY,  altKey,  shiftKey,  ctrlKey,
                             value: undefined,
                         }, console.log  );
@@ -482,7 +351,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                                 this: this.Document,
                                 _readOnly_: false,
                                 scriptContext: this._props.scriptContext,
-                                documentView: this.DocumentView(),
+                                documentView,
                                 clientX,
                                 clientY,
                                 shiftKey,
@@ -506,7 +375,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 const sendToBack = e.altKey;
                 this._singleClickFunc =
                     // prettier-ignore
-                    clickFunc ?? (() => (sendToBack ? this.DocumentView()._props.bringToFront(this.Document, true) : 
+                    clickFunc ?? (() => (sendToBack ? 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;
@@ -525,12 +394,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     onPointerDown = (e: React.PointerEvent): void => {
         if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return;
-        this._longPressSelector = setTimeout(() => {
-            if (DocumentView.LongPress) {
-                this._props.select(false);
-            }
-        }, 1000);
-        if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this.DocumentView();
+        this._longPressSelector = setTimeout(() => DocumentView.LongPress && this._props.select(false), 1000);
+        if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this._docView;
 
         this._downX = e.clientX;
         this._downY = e.clientY;
@@ -541,7 +406,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             if (
                 // prettier-ignore
                 (this._props.isDocumentActive?.() || this._props.isContentActive?.()) &&
-                !this._props.onBrowseClick?.() &&
+                !this._props.onBrowseClickScript?.() &&
                 !this.Document.ignoreClick &&
                 e.button === 0 &&
                 !Doc.IsInMyOverlay(this.layoutDoc)
@@ -587,38 +452,39 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (DocumentView.LongPress) e.preventDefault();
     };
 
-    @undoBatch
-    toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => {
+    toggleFollowLink = undoable((zoom?: boolean, setTargetToggle?: boolean): void => {
         const hadOnClick = this.Document.onClick;
         this.noOnClick();
         this.Document.onClick = hadOnClick ? undefined : FollowLinkScript();
         this.Document.waitForDoubleClickToClick = hadOnClick ? undefined : 'never';
-    };
-    @undoBatch
-    followLinkOnClick = () => {
+    }, 'toggle follow link');
+
+    followLinkOnClick = undoable(() => {
         this.Document.ignoreClick = false;
         this.Document.onClick = FollowLinkScript();
         this.Document.followLinkToggle = false;
         this.Document.followLinkZoom = false;
         this.Document.followLinkLocation = undefined;
-    };
-    @undoBatch
-    noOnClick = () => {
-        this.Document.ignoreClick = false;
-        this.Document.onClick = Doc.GetProto(this.Document).onClick = undefined;
-    };
-
-    @undoBatch deleteClicked = () => this._props.removeDocument?.(this.Document);
-    @undoBatch setToggleDetail = () =>
-        (this.Document.onClick = ScriptField.MakeScript(
-            `toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
-                .replace('layout_', '')
-                .replace(/^layout$/, 'detail')}")`,
-            { documentView: 'any' }
-        ));
+    }, 'follow link on click');
 
-    @undoBatch
-    drop = (e: Event, de: DragManager.DropEvent) => {
+    noOnClick = undoable(() => {
+        this.Document.ignoreClick = false;
+        this.Document.onClick = this.Document[DocData].onClick = undefined;
+    }, 'default on click');
+
+    deleteClicked = undoable(() => this._props.removeDocument?.(this.Document), 'delete doc');
+    setToggleDetail = undoable(
+        () =>
+            (this.Document.onClick = ScriptField.MakeScript(
+                `toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
+                    .replace('layout_', '')
+                    .replace(/^layout$/, 'detail')}")`,
+                { documentView: 'any' }
+            )),
+        'set toggle detail'
+    );
+
+    drop = undoable((e: Event, de: DragManager.DropEvent) => {
         if (this._props.dontRegisterView || this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false;
         if (this.Document === Doc.ActiveDashboard) {
             e.stopPropagation();
@@ -642,7 +508,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                     if (de.complete.linkDocument) {
                         de.complete.linkDocument.layout_isSvg = true;
-                        this.DocumentView().CollectionFreeFormView?.addDocument(de.complete.linkDocument);
+                        this._docView?.CollectionFreeFormView?.addDocument(de.complete.linkDocument);
                     }
                 }
                 e.stopPropagation();
@@ -650,11 +516,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
         }
         return false;
-    };
+    }, 'drop doc');
 
-    @undoBatch
-    makeIntoPortal = () => {
-        const portalLink = this.allLinks.find(d => d.link_anchor_1 === this.Document && d.link_relationship === 'portal to:portal from');
+    makeIntoPortal = undoable(() => {
+        const portalLink = this._allLinks.find(d => d.link_anchor_1 === this.Document && d.link_relationship === 'portal to:portal from');
         if (!portalLink) {
             DocUtils.MakeLink(
                 this.Document,
@@ -670,7 +535,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         this.Document.followLinkLocation = OpenWhere.lightbox;
         this.Document.onClick = FollowLinkScript();
-    };
+    }, 'make into portal');
 
     importDocument = () => {
         const input = document.createElement('input');
@@ -716,7 +581,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (e && !(e.nativeEvent as any).dash) {
             const onDisplay = () => {
-                if (this.Document.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this._props.isSelected() && SelectionManager.SelectView(this.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.select(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')) {
@@ -811,7 +676,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const constantItems: ContextMenuProps[] = [];
         if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking) {
             constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.Document) });
-            (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.DocumentView()), icon: 'users' });
+            (this.Document._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this._docView), icon: 'users' });
             if (this._props.removeDocument && Doc.ActiveDashboard !== this.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' });
@@ -877,63 +742,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15, undefined, undefined, undefined);
     };
 
-    @computed get _rootSelected() {
-        return this._props.isSelected() || BoolCast(this._props.TemplateDataDocument && this._props.rootSelected?.());
-    }
     rootSelected = () => this._rootSelected;
     panelHeight = () => this._props.PanelHeight() - this.headerMargin;
     screenToLocalContent = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin);
-    onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
+    onClickFunc = this.disableClickScriptFunc ? undefined : () => this.onClickHandler;
     setHeight = (height: number) => !this._props.suppressSetHeight && (this.layoutDoc._height = height);
-    setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
-    @observable _isContentActive: boolean | undefined = undefined;
-
+    setContentView = action((view: ViewBoxInterface) => (this._componentView = view));
     isContentActive = (): boolean | undefined => this._isContentActive;
     childFilters = () => [...this._props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
 
-    /// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
-    @computed get _contentPointerEvents() {
-        TraceMobx();
-        return this._props.contentPointerEvents ??
-            ((!this.disableClickScriptFunc && //
-                this.onClickHandler &&
-                !this._props.onBrowseClick?.() &&
-                this.isContentActive() !== true) ||
-                this.isContentActive() === false)
-            ? 'none'
-            : this.pointerEvents;
-    }
     contentPointerEvents = () => this._contentPointerEvents;
-    @computed get contents() {
-        TraceMobx();
-        const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
-        const noBackground = this.Document.isGroup && !this._props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
-        return (
-            <div
-                className="documentView-contentsView"
-                style={{
-                    pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? (this._mounted ? 'all' : 'none'),
-                    height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
-                }}>
-                <DocumentContentsView
-                    key={1}
-                    {...this.styleProps}
-                    fieldKey={this.finalLayoutKey}
-                    pointerEvents={this.contentPointerEvents}
-                    setContentView={this.setContentView}
-                    childFilters={this.childFilters}
-                    PanelHeight={this.panelHeight}
-                    setHeight={this.setHeight}
-                    isContentActive={this.isContentActive}
-                    ScreenToLocalTransform={this.screenToLocalContent}
-                    rootSelected={this.rootSelected}
-                    onClick={this.onClickFunc}
-                    setTitleFocus={this.setTitleFocus}
-                />
-                {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints}
-            </div>
-        );
-    }
 
     anchorPanelWidth = () => this._props.PanelWidth() || 1;
     anchorPanelHeight = () => this._props.PanelHeight() || 1;
@@ -950,33 +768,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         return this._props.styleProvider?.(doc, props, property);
     };
-    // We need to use allrelatedLinks to get not just links to the document as a whole, but links to
-    // anchors that are not rendered as DocumentViews (marked as 'layout_unrendered' with their 'annotationOn' set to this document).  e.g.,
-    //     - PDF text regions are rendered as an Annotations without generating a DocumentView, '
-    //     - RTF selections are rendered via Prosemirror and have a mark which contains the Document ID for the annotation link
-    //     - and links to PDF/Web docs at a certain scroll location never create an explicit view.
-    // For each of these, we create LinkAnchorBox's on the border of the DocumentView.
-    @computed get directLinks() {
-        TraceMobx();
-        return LinkManager.Instance.getAllRelatedLinks(this.Document).filter(
-            link =>
-                (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.Document);
-    }
-    hideLink = computedFn((link: Doc) => () => (link.link_displayLine = false));
-    @computed get allLinkEndpoints() {
+
+    removeLinkByHiding = (link: Doc) => () => (link.link_displayLine = false);
+    allLinkEndpoints = () => {
         // the small blue dots that mark the endpoints of links
-        TraceMobx();
         if (this._componentView instanceof KeyValueBox || this._props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this._props.dontRegisterView || this.layoutDoc.layout_unrendered) return null;
-        const filtered = DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines);
-        return filtered.map(link => (
+        return this.filteredLinks.map(link => (
             <div className="documentView-anchorCont" key={link[Id]}>
                 <DocumentView
                     {...this._props}
@@ -990,107 +787,54 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     hideCaptions={true}
                     hideLinkAnchors={true}
                     layout_fitWidth={returnTrue}
-                    removeDocument={this.hideLink(link)}
+                    removeDocument={this.removeLinkByHiding(link)}
                     styleProvider={this.anchorStyleProvider}
                     LayoutTemplate={undefined}
                     LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${LinkManager.anchorIndex(link, this.Document)}`)}
                 />
             </div>
         ));
-    }
-
-    static recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
-        let gumStream: any;
-        let recorder: any;
-        navigator.mediaDevices
-            .getUserMedia({
-                audio: true,
-            })
-            .then(function (stream) {
-                let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null);
-                if (audioTextAnnos) audioTextAnnos.push('');
-                else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List<string>(['']);
-                DictationManager.Controls.listen({
-                    interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value),
-                    continuous: { indefinite: false },
-                }).then(results => {
-                    if (results && [DictationManager.Controls.Infringed].includes(results)) {
-                        DictationManager.Controls.stop();
-                    }
-                    onEnd?.();
-                });
+    };
 
-                gumStream = stream;
-                recorder = new MediaRecorder(stream);
-                recorder.ondataavailable = async (e: any) => {
-                    const [{ result }] = await Networking.UploadFilesToServer({ file: e.data });
-                    if (!(result instanceof Error)) {
-                        const audioField = new AudioField(result.accessPaths.agnostic.client);
-                        const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null);
-                        if (audioAnnos === undefined) {
-                            dataDoc[field + '_audioAnnotations'] = new List([audioField]);
-                        } else {
-                            audioAnnos.push(audioField);
-                        }
-                    }
-                };
-                //runInAction(() => (dataDoc.audioAnnoState = 'recording'));
-                recorder.start();
-                const stopFunc = () => {
-                    recorder.stop();
-                    DictationManager.Controls.stop(false);
-                    runInAction(() => (dataDoc.audioAnnoState = 'stopped'));
-                    gumStream.getAudioTracks()[0].stop();
-                };
-                if (onRecording) onRecording(stopFunc);
-                else setTimeout(stopFunc, 5000);
-            });
-    }
-    playAnnotation = () => {
-        const self = this;
-        const audioAnnoState = this.dataDoc.audioAnnoState ?? 'stopped';
-        const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '_audioAnnotations'], listSpec(AudioField), null);
-        const anno = audioAnnos?.lastElement();
-        if (anno instanceof AudioField) {
-            switch (audioAnnoState) {
-                case 'stopped':
-                    this.dataDoc[AudioPlay] = new Howl({
-                        src: [anno.url.href],
-                        format: ['mp3'],
-                        autoplay: true,
-                        loop: false,
-                        volume: 0.5,
-                        onend: action(() => (self.dataDoc.audioAnnoState = 'stopped')),
-                    });
-                    this.dataDoc.audioAnnoState = 'playing';
-                    break;
-                case 'playing':
-                    this.dataDoc[AudioPlay]?.stop();
-                    this.dataDoc.audioAnnoState = 'stopped';
-                    break;
-            }
-        }
+    viewBoxContents = () => {
+        TraceMobx();
+        const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
+        const noBackground = this.Document.isGroup && !this._props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
+        return (
+            <div
+                className="documentView-contentsView"
+                style={{
+                    pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? (this._mounted ? 'all' : 'none'),
+                    height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
+                }}>
+                <DocumentContentsView
+                    {...this._props}
+                    layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')}
+                    pointerEvents={this.contentPointerEvents}
+                    setContentViewBox={this.setContentView}
+                    childFilters={this.childFilters}
+                    PanelHeight={this.panelHeight}
+                    setHeight={this.setHeight}
+                    isContentActive={this.isContentActive}
+                    ScreenToLocalTransform={this.screenToLocalContent}
+                    rootSelected={this.rootSelected}
+                    onClickScript={this.onClickFunc}
+                    setTitleFocus={this.setTitleFocus}
+                />
+                {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints()}
+            </div>
+        );
     };
 
     captionStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
-    @observable _changingTitleField = false;
-    @observable _dropDownInnerWidth = 0;
-    fieldsDropdown = (inputOptions: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
-        const filteredOptions = new Set(inputOptions);
-        const scaling = this.titleHeight / 30; /* height of Dropdown */
-        Object.entries(DocOptions)
-            .filter(opts => opts[1].filterable)
-            .forEach((pair: [string, FInfo]) => filteredOptions.add(pair[0]));
-        filteredOptions.add(StrCast(this.layoutDoc.layout_showTitle));
-        const options = Array.from(filteredOptions)
-            .filter(f => f)
-            .map(facet => ({ val: facet, text: facet }));
+    fieldsDropdown = (reqdFields: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
+        const filteredFields = Object.entries(DocOptions).reduce((set, [field, opts]) => (opts.filterable ? set.add(field) : set), new Set(reqdFields));
         return (
             <div style={{ width: dropdownWidth }}>
                 <div
-                    ref={action((r: any) => r && (this._dropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))}
+                    ref={action((r: any) => r && (this._titleDropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))}
                     onPointerDown={action(e => (this._changingTitleField = true))}
-                    style={{ width: 'max-content', transformOrigin: 'left', transform: `scale(${scaling})` }}>
+                    style={{ width: 'max-content', transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
                     <Dropdown
                         activeChanged={action(isOpen => !isOpen && (this._changingTitleField = false))}
                         selectedVal={placeholder}
@@ -1100,7 +844,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         type={Type.TERT}
                         closeOnSelect={true}
                         dropdownType={DropdownType.SELECT}
-                        items={options}
+                        items={Array.from(filteredFields).map(facet => ({ val: facet, text: facet }))}
                         width={100}
                         fillWidth
                     />
@@ -1108,43 +852,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>
         );
     };
-    @computed get innards() {
-        TraceMobx();
+    /**
+     * displays a 'title' at the top of a document. The title contents default to the 'title' field, but can be changed to one or more fields by
+     * setting layout_showTitle using the format:   field1[;field2[...][:hover]]
+     * from the UI, this is done by clicking the title field and prefixin the format with '#'.  eg.,  #field1[;field2;...][:hover]
+     **/
+    titleView = () => {
         const showTitle = this.layout_showTitle?.split(':')[0];
         const showTitleHover = this.layout_showTitle?.includes(':hover');
-        const captionView = !this.layout_showCaption ? null : (
-            <div
-                className="documentView-captionWrapper"
-                style={{
-                    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')),
-                }}>
-                <FormattedTextBox
-                    {...this._props}
-                    yPadding={10}
-                    xPadding={10}
-                    fieldKey={this.layout_showCaption}
-                    styleProvider={this.captionStyleProvider}
-                    dontRegisterView={true}
-                    noSidebar={true}
-                    dontScale={true}
-                    renderDepth={this._props.renderDepth}
-                    isContentActive={this.isContentActive}
-                />
-            </div>
-        );
-        const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.Document;
-        const background = StrCast(
-            this.layoutDoc.layout_headingColor,
-            StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor))
-        );
-        const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._dropDownInnerWidth * this.titleHeight) / 30) : 0;
-        const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
-        // displays a 'title' at the top of a document. The title contents default to the 'title' field, but can be changed to one or more fields by
-        // setting layout_showTitle using the format:   field1[;field2[...][:hover]]
-        // from the UI, this is done by clicking the title field and prefixin the format with '#'.  eg.,  #field1[;field2;...][:hover]
-        const titleView = !showTitle ? null : (
+
+        const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.Document;
+        const background = StrCast(
+            this.layoutDoc.layout_headingColor,
+            StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor))
+        );
+        const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._titleDropDownInnerWidth * this.titleHeight) / 30) : 0;
+        const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
+
+        return !showTitle ? null : (
             <div
                 className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
                 key="title"
@@ -1160,7 +885,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 {!dropdownWidth
                     ? null
                     : this.fieldsDropdown(
-                          [],
+                          [StrCast(this.layoutDoc.layout_showTitle)],
                           dropdownWidth,
                           StrCast(this.layoutDoc.layout_showTitle).split(':')[0],
                           action((field: string | number) => {
@@ -1206,19 +931,36 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 </div>
             </div>
         );
-        return this._props.hideTitle || (!showTitle && !this.layout_showCaption) ? (
-            this.contents
-        ) : (
-            <div className="documentView-styleWrapper">
-                {titleView}
-                {this.contents}
-                {captionView}
+    };
+
+    captionView = () => {
+        return !this.layout_showCaption ? null : (
+            <div
+                className="documentView-captionWrapper"
+                style={{
+                    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')),
+                }}>
+                <FormattedTextBox
+                    {...this._props}
+                    yPadding={10}
+                    xPadding={10}
+                    fieldKey={this.layout_showCaption}
+                    styleProvider={this.captionStyleProvider}
+                    dontRegisterView={true}
+                    noSidebar={true}
+                    dontScale={true}
+                    renderDepth={this._props.renderDepth}
+                    isContentActive={this.isContentActive}
+                />
             </div>
         );
-    }
+    };
 
     renderDoc = (style: object) => {
         TraceMobx();
+        const showTitle = this.layout_showTitle?.split(':')[0];
         return !DocCast(this.Document) || GetEffectiveAcl(this.dataDoc) === AclPrivate
             ? null
             : this.docContents ?? (
@@ -1234,49 +976,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                           fontFamily: StrCast(this.Document._text_fontFamily, 'inherit'),
                           fontSize: Cast(this.Document._text_fontSize, 'string', null),
                           transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
-                          transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this.animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
+                          transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this.animateScaleTime() / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
                       }}>
-                      {this.innards}
+                      {this._props.hideTitle || (!showTitle && !this.layout_showCaption) ? (
+                          this.viewBoxContents()
+                      ) : (
+                          <div className="documentView-styleWrapper">
+                              {this.titleView()}
+                              {this.viewBoxContents()}
+                              {this.captionView()}
+                          </div>
+                      )}
                       {this.widgetDecorations ?? null}
                   </div>
               );
     };
 
-    /**
-     * returns an entrance animation effect function to wrap a JSX element
-     * @param presEffectDoc presentation effects document that specifies the animation effect parameters
-     * @returns a function that will wrap a JSX animation element wrapping any JSX element
-     */
-    public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) {
-        const dir = presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection;
-        const effectProps = {
-            left: dir === PresEffectDirection.Left,
-            right: dir === PresEffectDirection.Right,
-            top: dir === PresEffectDirection.Top,
-            bottom: dir === PresEffectDirection.Bottom,
-            opposite: true,
-            delay: 0,
-            duration: Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
-        };
-        //prettier-ignore
-        switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
-            default: 
-            case PresEffect.None:       return renderDoc;
-            case PresEffect.Zoom:       return <Zoom {...effectProps}>{renderDoc}</Zoom>;
-            case PresEffect.Fade:       return <Fade {...effectProps}>{renderDoc}</Fade>;
-            case PresEffect.Flip:       return <Flip {...effectProps}>{renderDoc}</Flip>;
-            case PresEffect.Rotate:     return <Rotate {...effectProps}>{renderDoc}</Rotate>;
-            case PresEffect.Bounce:     return <Bounce {...effectProps}>{renderDoc}</Bounce>;
-            case PresEffect.Roll:       return <Roll {...effectProps}>{renderDoc}</Roll>;
-            case PresEffect.Lightspeed: return <JackInTheBox {...effectProps}>{renderDoc}</JackInTheBox>;
-        }
-    }
-    @computed get highlighting() {
-        return this._props.styleProvider?.(this.Document, { ...this._props, fieldKey: '' }, StyleProp.Highlighting);
-    }
-    @computed get borderPath() {
-        return this._props.styleProvider?.(this.Document, { ...this._props, fieldKey: '' }, StyleProp.BorderPath);
-    }
     render() {
         TraceMobx();
         const highlighting = this.highlighting;
@@ -1304,10 +1019,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 onClick={this.onClick}
                 onPointerEnter={e => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)}
                 onPointerOver={e => (!SnappingManager.IsDragging || SnappingManager.CanEmbed) && Doc.BrushDoc(this.Document)}
-                onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.Document)}
+                onPointerLeave={e => !isParentOf(this._contentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.Document)}
                 style={{
                     borderRadius: this.borderRounding,
-                    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)
+                    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.Document[Animation], this.Document)}
@@ -1316,176 +1031,124 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>
         );
     }
-}
 
-@observer
-export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
-    public static ROOT_DIV = 'documentView-effectsWrapper';
-
-    constructor(props: any) {
-        super(props);
-        makeObservable(this);
+    /**
+     * returns an entrance animation effect function to wrap a JSX element
+     * @param presEffectDoc presentation effects document that specifies the animation effect parameters
+     * @returns a function that will wrap a JSX animation element wrapping any JSX element
+     */
+    public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) {
+        const dir = presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection;
+        const effectProps = {
+            left: dir === PresEffectDirection.Left,
+            right: dir === PresEffectDirection.Right,
+            top: dir === PresEffectDirection.Top,
+            bottom: dir === PresEffectDirection.Bottom,
+            opposite: true,
+            delay: 0,
+            duration: Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)),
+        };
+        //prettier-ignore
+        switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
+            default: 
+            case PresEffect.None:       return renderDoc;
+            case PresEffect.Zoom:       return <Zoom {...effectProps}>{renderDoc}</Zoom>;
+            case PresEffect.Fade:       return <Fade {...effectProps}>{renderDoc}</Fade>;
+            case PresEffect.Flip:       return <Flip {...effectProps}>{renderDoc}</Flip>;
+            case PresEffect.Rotate:     return <Rotate {...effectProps}>{renderDoc}</Rotate>;
+            case PresEffect.Bounce:     return <Bounce {...effectProps}>{renderDoc}</Bounce>;
+            case PresEffect.Roll:       return <Roll {...effectProps}>{renderDoc}</Roll>;
+            case PresEffect.Lightspeed: return <JackInTheBox {...effectProps}>{renderDoc}</JackInTheBox>;
+        }
     }
+    public static recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
+        let gumStream: any;
+        let recorder: any;
+        navigator.mediaDevices
+            .getUserMedia({
+                audio: true,
+            })
+            .then(function (stream) {
+                let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null);
+                if (audioTextAnnos) audioTextAnnos.push('');
+                else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List<string>(['']);
+                DictationManager.Controls.listen({
+                    interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value),
+                    continuous: { indefinite: false },
+                }).then(results => {
+                    if (results && [DictationManager.Controls.Infringed].includes(results)) {
+                        DictationManager.Controls.stop();
+                    }
+                    onEnd?.();
+                });
 
-    @observable _selected = false;
-    public get IsSelected() {
-        return this._selected;
-    }
-    public set IsSelected(val) {
-        runInAction(() => (this._selected = val));
-    }
-    @observable public static LongPress = false;
-    @computed public static get exploreMode() {
-        return () => (SnappingManager.ExploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined);
+                gumStream = stream;
+                recorder = new MediaRecorder(stream);
+                recorder.ondataavailable = async (e: any) => {
+                    const [{ result }] = await Networking.UploadFilesToServer({ file: e.data });
+                    if (!(result instanceof Error)) {
+                        const audioField = new AudioField(result.accessPaths.agnostic.client);
+                        const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null);
+                        if (audioAnnos === undefined) {
+                            dataDoc[field + '_audioAnnotations'] = new List([audioField]);
+                        } else {
+                            audioAnnos.push(audioField);
+                        }
+                    }
+                };
+                //runInAction(() => (dataDoc.audioAnnoState = 'recording'));
+                recorder.start();
+                const stopFunc = () => {
+                    recorder.stop();
+                    DictationManager.Controls.stop(false);
+                    runInAction(() => (dataDoc.audioAnnoState = 'stopped'));
+                    gumStream.getAudioTracks()[0].stop();
+                };
+                if (onRecording) onRecording(stopFunc);
+                else setTimeout(stopFunc, 5000);
+            });
     }
-    @observable private _docViewInternal: DocumentViewInternal | undefined | null = undefined;
-    @observable private _htmlOverlayText: Opt<string> = undefined;
-    @observable private _isHovering = false;
-    private _htmlOverlayEffect: Opt<Doc>;
+}
 
-    public get displayName() {
-        return 'DocumentView(' + this.Document?.title + ')';
-    } // this makes mobx trace() statements more descriptive
+@observer
+export class DocumentView extends DocComponent<DocumentViewProps>() {
+    public static ROOT_DIV = 'documentView-effectsWrapper';
+    public get displayName() {  return 'DocumentView(' + this.Document?.title + ')';  } // prettier-ignore
     public ContentRef = React.createRef<HTMLDivElement>();
-    public ViewTimer: NodeJS.Timeout | undefined; // timer for res
-    public AnimEffectTimer: NodeJS.Timeout | undefined; // timer for res
+    private _htmlOverlayEffect: Opt<Doc>;
     private _disposers: { [name: string]: IReactionDisposer } = {};
-    public clearViewTransition = () => {
-        this.ViewTimer && clearTimeout(this.ViewTimer);
-        this.layoutDoc._viewTransition = undefined;
-    };
-    playAnnotation = () => this._docViewInternal?.playAnnotation();
-    noOnClick = () => this._docViewInternal?.noOnClick();
-    makeIntoPortal = () => this._docViewInternal?.makeIntoPortal();
-    setToggleDetail = () => this._docViewInternal?.setToggleDetail();
-    onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => this._docViewInternal?.onContextMenu?.(e, pageX, pageY);
-    cleanupPointerEvents = () => this._docViewInternal?.cleanupPointerEvents();
-    public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this._docViewInternal?.startDragging(x, y, dropAction, hideSource);
+    private _viewTimer: NodeJS.Timeout | undefined;
+    private _animEffectTimer: NodeJS.Timeout | undefined;
 
-    public showContextMenu = (pageX: number, pageY: number) => this._docViewInternal?.onContextMenu(undefined, pageX, pageY);
-
-    public setTextHtmlOverlay = action((text: string | undefined, effect?: Doc) => {
-        this._htmlOverlayText = text;
-        this._htmlOverlayEffect = effect;
-    });
-    public setAnimateScaling = action((scale: number, time?: number) => {
-        if (this._docViewInternal) {
-            this._docViewInternal._animateScalingTo = scale;
-            this._docViewInternal._animateScaleTime = time;
-        }
-    });
-    public setAnimEffect = (presEffect: Doc, timeInMs: number, afterTrans?: () => void) => {
-        this.AnimEffectTimer && clearTimeout(this.AnimEffectTimer);
-        this.Document[Animation] = presEffect;
-        this.AnimEffectTimer = setTimeout(() => (this.Document[Animation] = undefined), timeInMs);
-    };
-    public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => {
-        this.layoutDoc._viewTransition = `${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.Document._dataTransition = 'inherit';
-            afterTrans?.();
-        }, timeInMs + 10));
-    };
-    public static SetViewTransition(docs: Doc[], transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) {
-        docs.forEach(doc => {
-            doc._viewTransition = `${transProp} ${timeInMs}ms`;
-            dataTrans && (doc.dataTransition = `${transProp} ${timeInMs}ms`);
-        });
-        return setTimeout(
-            () =>
-                docs.forEach(doc => {
-                    doc._viewTransition = undefined;
-                    dataTrans && (doc.dataTransition = 'inherit');
-                    afterTrans?.();
-                }),
-            timeInMs + 10
-        );
+    @computed public static get exploreMode() {
+        return () => (SnappingManager.ExploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined);
     }
 
-    // shows a stacking view collection (by default, but the user can change) of all documents linked to the source
-    public static showBackLinks(linkAnchor: Doc) {
-        const docId = Doc.CurrentUserEmail + Doc.GetProto(linkAnchor)[Id] + '-pivotish';
-        // prettier-ignore
-        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, onViewMounted: ScriptField.MakeScript('updateLinkCollection(this, this.target)') }, docId)
-            )
-        );
+    constructor(props: DocumentViewProps) {
+        super(props);
+        makeObservable(this);
     }
-    toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => this._docViewInternal?.toggleFollowLink(zoom, setTargetToggle);
 
-    get Document() {
-        return this._props.Document;
-    }
-    get topMost() {
-        return this._props.renderDepth === 0;
-    }
-    get dataDoc() {
-        return this._docViewInternal?.dataDoc ?? this.Document;
-    }
-    get ContentDiv() {
-        return this._docViewInternal?.ContentDiv;
-    }
-    get ComponentView() {
-        return this._docViewInternal?._componentView;
-    }
-    get allLinks() {
-        return (this._docViewInternal?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document);
-    }
-    get LayoutFieldKey() {
-        return this._docViewInternal?.LayoutFieldKey || 'layout';
-    }
-    @computed get layout_fitWidth() {
-        return this._props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth;
-    }
-    @computed get anchorViewDoc() {
-        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.hideLinkButton ||
-            this._props.renderDepth === -1 || //
-            (this.IsSelected && this._props.renderDepth) ||
-            !this._isHovering ||
-            (!this.IsSelected && this.layoutDoc.layout_hideLinkButton) ||
-            SnappingManager.IsDragging ||
-            SnappingManager.IsResizing
-        );
-    }
-    hideLinkCount = () => (this.hideLinkButton ? true : false);
+    // want the htmloverlay to be able to fade in but we also want it to be display 'none' until it is needed.
+    // unfortunately, CSS can't transition animate any properties for something that is display 'none'.
+    // so we need to first activate the div, then, after a render timeout, start the opacity transition.
+    @observable private _enableHtmlOverlayTransitions: boolean = false;
+    @observable private _docViewInternal: DocumentViewInternal | undefined | null = undefined;
+    @observable private _htmlOverlayText: Opt<string> = undefined;
+    @observable private _isHovering = false;
+    @observable private _selected = false;
+    @observable public static LongPress = false;
 
-    @computed get linkCountView() {
-        return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.screenToLocalScale} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
-    }
-    /**
-     * path of DocumentViews hat contains this DocumentView (does not includes this DocumentView thouhg)
-     */
-    @computed get containerViewPath() {
-        return this._props.containerViewPath;
-    }
-    @computed get layoutDoc() {
-        return Doc.Layout(this.Document, this._props.LayoutTemplate?.());
-    }
-    @computed get nativeWidth() {
-        return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth));
-    }
-    @computed get nativeHeight() {
-        return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth));
-    }
-    @computed get shouldNotScale() {
+    @computed private get shouldNotScale() {
         return (this.layout_fitWidth && !this.nativeWidth) || this._props.LayoutTemplateString?.includes(KeyValueBox.name) || [CollectionViewType.Docking].includes(this.Document._type_collection as any);
     }
-    @computed get effectiveNativeWidth() {
+    @computed private get effectiveNativeWidth() {
         return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width);
     }
-    @computed get effectiveNativeHeight() {
+    @computed private get effectiveNativeHeight() {
         return this.shouldNotScale ? 0 : this.nativeHeight || NumCast(this.layoutDoc.height);
     }
-    @computed get nativeScaling() {
+    @computed private get nativeScaling() {
         if (this.shouldNotScale) return 1;
         const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
         if (this.layout_fitWidth || this._props.PanelHeight() / (this.effectiveNativeHeight || 1) > this._props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
@@ -1493,19 +1156,19 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
         }
         return Math.max(minTextScale, this._props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled
     }
-    @computed get panelWidth() {
+    @computed private get panelWidth() {
         return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this._props.PanelWidth();
     }
-    @computed get panelHeight() {
+    @computed private get panelHeight() {
         if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.layout_reflowVertical)) {
             return Math.min(this._props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
         }
         return this._props.PanelHeight();
     }
-    @computed get Xshift() {
+    @computed private get Xshift() {
         return this.effectiveNativeWidth ? Math.max(0, (this._props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2) : 0;
     }
-    @computed get Yshift() {
+    @computed private get Yshift() {
         return this.effectiveNativeWidth &&
             this.effectiveNativeHeight &&
             Math.abs(this.Xshift) < 0.001 &&
@@ -1513,41 +1176,99 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
             ? Math.max(0, (this._props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2)
             : 0;
     }
-    @computed get centeringX() {
-        return this._props.dontCenter?.includes('x') ? 0 : this.Xshift;
+    @computed private get hideLinkButton() {
+        return (
+            this._props.hideLinkButton ||
+            this._props.renderDepth === -1 || //
+            (this.IsSelected && this._props.renderDepth) ||
+            !this._isHovering ||
+            (!this.IsSelected && this.layoutDoc.layout_hideLinkButton) ||
+            SnappingManager.IsDragging ||
+            SnappingManager.IsResizing
+        );
+    }
+
+    componentDidMount() {
+        runInAction(() => this.Document[DocViews].add(this));
+        this._disposers.onViewMounted = reaction(() => ScriptCast(this.Document.onViewMounted)?.script?.run({ this: this.Document }).result, emptyFunction);
+        !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.AddView(this);
+    }
+
+    componentWillUnmount() {
+        runInAction(() => this.Document[DocViews].delete(this));
+        Object.values(this._disposers).forEach(disposer => disposer?.());
+        !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
     }
-    @computed get centeringY() {
-        return this._props.dontCenter?.includes('y') ? 0 : this.Yshift;
+
+    public set IsSelected(val)  { runInAction(() => (this._selected = val)); } // prettier-ignore
+    public get IsSelected()     { return this._selected; } // prettier-ignore
+    public get topMost()        { return this._props.renderDepth === 0; } // prettier-ignore
+    public get ContentDiv()     { return this._docViewInternal?._contentDiv; } // prettier-ignore
+    public get ComponentView()  { return this._docViewInternal?._componentView; } // prettier-ignore
+    public get allLinks()       { return this._docViewInternal?._allLinks ?? []; } // prettier-ignore
+
+    get LayoutFieldKey() {
+        return Doc.LayoutFieldKey(this.Document, this._props.LayoutTemplateString);
     }
 
-    @computed get CollectionFreeFormView() {
-        return this.CollectionFreeFormDocumentView?.CollectionFreeFormView;
+    @computed get layout_fitWidth() {
+        return this._props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth;
     }
-    @computed get CollectionFreeFormDocumentView() {
-        return this._props.CollectionFreeFormDocumentView?.();
+    @computed get anchorViewDoc() {
+        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;
     }
 
-    public toggleNativeDimensions = () => this._docViewInternal && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this._docViewInternal.NativeDimScaling, this._props.PanelWidth(), this._props.PanelHeight());
-    public getBounds = () => {
-        if (!this._docViewInternal?.ContentDiv || this._props.treeViewDoc || Doc.AreProtosEqual(this.Document, Doc.UserDoc())) {
+    @computed get getBounds() {
+        if (!this._docViewInternal?._contentDiv || this._props.treeViewDoc || Doc.AreProtosEqual(this.Document, Doc.UserDoc())) {
             return undefined;
         }
         if (this._docViewInternal._componentView?.screenBounds?.()) {
             return this._docViewInternal._componentView.screenBounds();
         }
-        const xf = this._docViewInternal._props.ScreenToLocalTransform().scale(this.nativeScaling).inverse();
+        const xf = this.screenToContentsTransform().scale(this.nativeScaling).inverse();
         const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
 
-        if (this._docViewInternal._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const docuBox = this._docViewInternal.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
+        if (this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
+            const docuBox = this._docViewInternal._contentDiv.getElementsByClassName('linkAnchorBox-cont');
             if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
         }
         return { left, top, right, bottom };
+    }
+
+    @computed get nativeWidth() {
+        return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth));
+    }
+    @computed get nativeHeight() {
+        return this._props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this._props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this._props.TemplateDataDocument, !this.layout_fitWidth));
+    }
+    @computed public get centeringX() { return this._props.dontCenter?.includes('x') ? 0 : this.Xshift; } // prettier-ignore
+    @computed public get centeringY() { return this._props.dontCenter?.includes('y') ? 0 : this.Yshift; } // prettier-ignore
+
+    /**
+     * path of DocumentViews hat contains this DocumentView (does not includes this DocumentView thouhg)
+     */
+    public get containerViewPath() { return this._props.containerViewPath; } // prettier-ignore
+    public get CollectionFreeFormView() { return this.CollectionFreeFormDocumentView?.CollectionFreeFormView; } // prettier-ignore
+    public get CollectionFreeFormDocumentView() { return this._props.CollectionFreeFormDocumentView?.(); } // prettier-ignore
+
+    public clearViewTransition = () => {
+        this._viewTimer && clearTimeout(this._viewTimer);
+        this.layoutDoc._viewTransition = undefined;
     };
+    public noOnClick = () => this._docViewInternal?.noOnClick();
+    public makeIntoPortal = () => this._docViewInternal?.makeIntoPortal();
+    public toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => this._docViewInternal?.toggleFollowLink(zoom, setTargetToggle);
+    public setToggleDetail = () => this._docViewInternal?.setToggleDetail();
+    public onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => this._docViewInternal?.onContextMenu?.(e, pageX, pageY);
+    public cleanupPointerEvents = () => this._docViewInternal?.cleanupPointerEvents();
+    public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this._docViewInternal?.startDragging(x, y, dropAction, hideSource);
+    public showContextMenu = (pageX: number, pageY: number) => this._docViewInternal?.onContextMenu(undefined, pageX, pageY);
+
+    public toggleNativeDimensions = () => this._docViewInternal && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.NativeDimScaling() ?? 1, this._props.PanelWidth(), this._props.PanelHeight());
 
     public iconify(finished?: () => void, animateTime?: number) {
         this.ComponentView?.updateIcon?.();
-        const animTime = this._docViewInternal?._animateScaleTime;
+        const animTime = this._docViewInternal?.animateScaleTime();
         runInAction(() => this._docViewInternal && animateTime !== undefined && (this._docViewInternal._animateScaleTime = animateTime));
         const finalFinished = action(() => {
             finished?.();
@@ -1564,13 +1285,58 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
             this._props.bringToFront(this.Document);
         }
     }
-    @undoBatch
-    setCustomView = (custom: boolean, layout: string): void => {
+
+    public playAnnotation = () => {
+        const self = this;
+        const audioAnnoState = this.dataDoc.audioAnnoState ?? 'stopped';
+        const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '_audioAnnotations'], listSpec(AudioField), null);
+        const anno = audioAnnos?.lastElement();
+        if (anno instanceof AudioField) {
+            switch (audioAnnoState) {
+                case 'stopped':
+                    this.dataDoc[AudioPlay] = new Howl({
+                        src: [anno.url.href],
+                        format: ['mp3'],
+                        autoplay: true,
+                        loop: false,
+                        volume: 0.5,
+                        onend: action(() => (self.dataDoc.audioAnnoState = 'stopped')),
+                    });
+                    this.dataDoc.audioAnnoState = 'playing';
+                    break;
+                case 'playing':
+                    this.dataDoc[AudioPlay]?.stop();
+                    this.dataDoc.audioAnnoState = 'stopped';
+                    break;
+            }
+        }
+    };
+
+    public setTextHtmlOverlay = action((text: string | undefined, effect?: Doc) => {
+        this._htmlOverlayText = text;
+        this._htmlOverlayEffect = effect;
+    });
+    public setAnimateScaling = action((scale: number, time?: number) => {
+        if (this._docViewInternal) {
+            this._docViewInternal._animateScalingTo = scale;
+            this._docViewInternal._animateScaleTime = time;
+        }
+    });
+    public setAnimEffect = (presEffect: Doc, timeInMs: number, afterTrans?: () => void) => {
+        this._animEffectTimer && clearTimeout(this._animEffectTimer);
+        this.Document[Animation] = presEffect;
+        this._animEffectTimer = setTimeout(() => (this.Document[Animation] = undefined), timeInMs);
+    };
+    public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => {
+        this._viewTimer = DocumentView.SetViewTransition([this.layoutDoc], transProp, timeInMs, this._viewTimer, afterTrans, dataTrans);
+    };
+
+    public setCustomView = undoable((custom: boolean, layout: string): void => {
         Doc.setNativeView(this.Document);
         custom && DocUtils.makeCustomViewClicked(this.Document, Docs.Create.StackingDocument, layout, undefined);
-    };
+    }, 'set custom view');
 
-    switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
+    public switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
         runInAction(() => this._docViewInternal && (this._docViewInternal._animateScalingTo = 0.1)); // shrink doc
         setTimeout(
             action(() => {
@@ -1585,16 +1351,19 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                         this._docViewInternal && (this._docViewInternal._animateScalingTo = 0);
                         finished?.();
                     }),
-                    this._docViewInternal ? Math.max(0, this._docViewInternal.animateScaleTime - 10) : 0
+                    Math.max(0, (this._docViewInternal?.animateScaleTime() ?? 0) - 10)
                 );
             }),
-            this._docViewInternal ? Math.max(0, this._docViewInternal?.animateScaleTime - 10) : 0
+            Math.max(0, (this._docViewInternal?.animateScaleTime() ?? 0) - 10)
         );
     };
+    /**
+     * @returns a hierarchy path through  the nested DocumentViews that display this view.  The last element of the path is this view.
+     */
+    public docViewPath = () => (this.containerViewPath ? [...this.containerViewPath(), this] : [this]);
 
     layout_fitWidthFunc = (doc: Doc) => BoolCast(this.layout_fitWidth);
     screenToLocalScale = () => this._props.ScreenToLocalTransform().Scale;
-    docViewPath = () => (this.containerViewPath ? [...this.containerViewPath(), this] : [this]);
     isSelected = () => this.IsSelected;
     select = (extendSelection: boolean, focusSelection?: boolean) => {
         if (this.IsSelected && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this);
@@ -1615,6 +1384,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
     PanelWidth = () => this.panelWidth;
     PanelHeight = () => this.panelHeight;
     NativeDimScaling = () => this.nativeScaling;
+    hideLinkCount = () => (this.hideLinkButton ? true : false);
     selfView = () => this;
     /**
      * @returns Transform to the document view (in the coordinate system of whatever contains the DocumentView)
@@ -1629,34 +1399,19 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
             .translate(-this.centeringX, -this.centeringY)
             .scale(1 / this.nativeScaling);
 
-    componentDidMount() {
-        runInAction(() => this.Document[DocViews].add(this));
-        this._disposers.onViewMounted = reaction(() => ScriptCast(this.Document.onViewMounted)?.script?.run({ this: this.Document }).result, emptyFunction);
-        !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.AddView(this);
-    }
-
-    componentWillUnmount() {
-        runInAction(() => this.Document[DocViews].delete(this));
-        Object.values(this._disposers).forEach(disposer => disposer?.());
-        !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
-    }
-    // want the htmloverlay to be able to fade in but we also want it to be display 'none' until it is needed.
-    // unfortunately, CSS can't transition animate any properties for something that is display 'none'.
-    // so we need to first activate the div, then, after a render timeout, start the opacity transition.
-    @observable enableHtmlOverlayTransitions: boolean = false;
-    @computed get htmlOverlay() {
+    htmlOverlay = () => {
         const effect = StrCast(this._htmlOverlayEffect?.presentation_effect, StrCast(this._htmlOverlayEffect?.followLinkAnimEffect));
         return (
             <div
                 className="documentView-htmlOverlay"
                 ref={r => {
                     const val = r?.style.display !== 'none'; // if the outer overlay has been displayed, trigger the innner div to start it's opacity fade in transition
-                    if (r && val !== this.enableHtmlOverlayTransitions) {
-                        setTimeout(action(() => (this.enableHtmlOverlayTransitions = val)));
+                    if (r && val !== this._enableHtmlOverlayTransitions) {
+                        setTimeout(action(() => (this._enableHtmlOverlayTransitions = val)));
                     }
                 }}
                 style={{ display: !this._htmlOverlayText ? 'none' : undefined }}>
-                <div className="documentView-htmlOverlayInner" style={{ transition: `all 500ms`, opacity: this.enableHtmlOverlayTransitions ? 0.9 : 0 }}>
+                <div className="documentView-htmlOverlayInner" style={{ transition: `all 500ms`, opacity: this._enableHtmlOverlayTransitions ? 0.9 : 0 }}>
                     {DocumentViewInternal.AnimationEffect(
                         <div className="webBox-textHighlight">
                             <ObserverJsxParser autoCloseVoidElements={true} key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this._htmlOverlayText)} />
@@ -1667,11 +1422,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                 </div>
             </div>
         );
-    }
-
-    @computed get infoUI() {
-        return this.ComponentView?.infoUI?.();
-    }
+    };
 
     render() {
         TraceMobx();
@@ -1685,14 +1436,14 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                         className="contentFittingDocumentView-previewDoc"
                         ref={this.ContentRef}
                         style={{
-                            transition: 'inherit', // this._props.dataTransition,
                             transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
                             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}
-                            docViewPublic={this.selfView}
+                            fieldKey={this.LayoutFieldKey}
+                            DocumentView={this.selfView}
                             docViewPath={this.docViewPath}
                             PanelWidth={this.PanelWidth}
                             PanelHeight={this.PanelHeight}
@@ -1706,15 +1457,44 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
                             focus={this._props.focus || emptyFunction}
                             ref={action((r: DocumentViewInternal | null) => r && (this._docViewInternal = r))}
                         />
-                        {this.htmlOverlay}
-                        {this.infoUI}
+                        {this.htmlOverlay()}
+                        {this.ComponentView?.infoUI?.()}
                     </div>
                 )}
-
-                {this.linkCountView}
+                {/* display link count button */}
+                <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.screenToLocalScale} OnHover={true} Bottom={this.topMost} ShowCount={true} />
             </div>
         );
     }
+
+    public static SetViewTransition(docs: Doc[], transProp: string, timeInMs: number, timer?: NodeJS.Timeout | undefined, afterTrans?: () => void, dataTrans = false) {
+        docs.forEach(doc => {
+            doc._viewTransition = `${transProp} ${timeInMs}ms`;
+            dataTrans && (doc.dataTransition = `${transProp} ${timeInMs}ms`);
+        });
+        timer && clearTimeout(timer);
+        return setTimeout(
+            () =>
+                docs.forEach(doc => {
+                    doc._viewTransition = undefined;
+                    dataTrans && (doc.dataTransition = 'inherit');
+                    afterTrans?.();
+                }),
+            timeInMs + 10
+        );
+    }
+
+    // shows a stacking view collection (by default, but the user can change) of all documents linked to the source
+    public static showBackLinks(linkAnchor: Doc) {
+        const docId = Doc.CurrentUserEmail + Doc.GetProto(linkAnchor)[Id] + '-pivotish';
+        // prettier-ignore
+        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, onViewMounted: ScriptField.MakeScript('updateLinkCollection(this, this.target)') }, docId)
+            )
+        );
+    }
 }
 
 ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index cad76ddf7..2e03a766a 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -20,13 +20,13 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static SelectOnLoad: string = '';
     _ref: React.RefObject<EquationEditor> = React.createRef();
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()))) {
             this._props.select(false);
 
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index b456807d7..5bb295565 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,26 +1,108 @@
-import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DateField } from '../../../fields/DateField';
-import { Doc, Field, FieldResult, Opt } from '../../../fields/Doc';
+import { Doc, Field, Opt } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
 import { WebField } from '../../../fields/URLField';
-import { DocumentView, DocumentViewInternalSharedProps, DocumentViewSharedProps } from './DocumentView';
+import { dropActionType } from '../../util/DragManager';
+import { Transform } from '../../util/Transform';
+import { ViewBoxInterface } from '../DocComponent';
+import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
+import { DocumentView, OpenWhere } from './DocumentView';
+import { PinProps } from './trails';
 
+export interface FocusViewOptions {
+    willPan?: boolean; // determines whether to pan to target document
+    willZoomCentered?: boolean; // determines whether to zoom in on target document.  if zoomScale is 0, this just centers the document
+    zoomScale?: number; // percent of containing frame to zoom into document
+    zoomTime?: number;
+    didMove?: boolean; // whether a document was changed during the showDocument process
+    docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom.  when this happens the docTransform must accumulate all the transforms of each level of the hierarchy
+    instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom)
+    preview?: boolean; // whether changes should be previewed by the componentView or written to the document
+    effect?: Doc; // animation effect for focus
+    noSelect?: boolean; // whether target should be selected after focusing
+    playAudio?: boolean; // whether to play audio annotation on focus
+    playMedia?: boolean; // whether to play start target videos
+    openLocation?: OpenWhere; // where to open a missing document
+    zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
+    toggleTarget?: boolean; // whether to toggle target on and off
+    anchorDoc?: Doc; // doc containing anchor info to apply at end of focus to target doc
+    easeFunc?: 'linear' | 'ease'; // transition method for scrolling
+}
+export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>;
+export type StyleProviderFuncType = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => any;
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
 // However, that only happens because the properties are "defined" in the markup for the field view.
 // See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc.
 //
-export interface FieldViewProps extends DocumentViewSharedProps, DocumentViewInternalSharedProps {
-    // FieldView specific props that are not part of DocumentView props
-    fieldKey: string;
-
-    setHeight?: (height: number) => void;
-    onBrowseClick?: () => ScriptField | undefined;
+export interface FieldViewSharedProps {
+    Document: Doc;
+    LayoutTemplateString?: string;
+    LayoutTemplate?: () => Opt<Doc>;
+    TemplateDataDocument?: Doc;
+    renderDepth: number;
+    scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
+    treeViewDoc?: Doc;
+    xPadding?: number;
+    yPadding?: number;
+    dontRegisterView?: boolean;
+    dropAction?: dropActionType;
+    dragAction?: dropActionType;
+    forceAutoHeight?: boolean;
+    ignoreAutoHeight?: boolean;
+    disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
+    CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
+    containerViewPath?: () => DocumentView[];
+    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
+    isGroupActive?: () => string | undefined; // is this document part of a group that is active
+    setContentViewBox?: (view: ViewBoxInterface) => any; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox
+    PanelWidth: () => number;
+    PanelHeight: () => number;
+    isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
+    isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
+    childFilters: () => string[];
+    childFiltersByRanges: () => string[];
+    styleProvider: Opt<StyleProviderFuncType>;
+    setTitleFocus?: () => void;
+    focus: FocusFuncType;
+    onClickScript?: () => ScriptField;
+    onDoubleClickScript?: () => ScriptField;
+    onPointerDownScript?: () => ScriptField;
+    onPointerUpScript?: () => ScriptField;
+    onBrowseClickScript?: () => ScriptField | undefined;
     onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
+    layout_fitWidth?: (doc: Doc) => boolean | undefined;
+    searchFilterDocs: () => Doc[];
+    layout_showTitle?: () => string;
+    whenChildContentsActiveChanged: (isActive: boolean) => void;
+    rootSelected?: () => boolean; // whether the root of a template has been selected
+    addDocTab: (doc: Doc, where: OpenWhere) => boolean;
+    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+    addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean;
+    removeDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean;
+    moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => boolean;
+    pinToPres: (document: Doc, pinProps: PinProps) => void;
+    ScreenToLocalTransform: () => Transform;
+    bringToFront: (doc: Doc, sendToBack?: boolean) => void;
+    waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
+    defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
     pointerEvents?: () => Opt<string>;
+}
+
+/**
+ * FieldView specific props that are not shared with DocumentView props
+ *  */
+export interface FieldViewProps extends FieldViewSharedProps {
+    DocumentView?: () => DocumentView;
+    fieldKey: string;
+    isSelected: () => boolean;
+    select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
+    docViewPath: () => DocumentView[];
+    setHeight?: (height: number) => void;
+    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields
@@ -30,9 +112,9 @@ export interface FieldViewProps extends DocumentViewSharedProps, DocumentViewInt
     color?: string;
     height?: number;
     width?: number;
+    dontSelectOnLoad?: boolean; // suppress selecting (e.g.,. text box) when loaded (and mark as not being associated with scrollTop document field)
     noSidebar?: boolean;
     dontScale?: boolean;
-    dontSelectOnLoad?: boolean; // suppress selecting (e.g.,. text box) when loaded (and mark as not being associated with scrollTop document field)
 }
 
 @observer
@@ -41,13 +123,8 @@ export class FieldView extends React.Component<FieldViewProps> {
         return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={'data'} />"
     }
 
-    @computed
-    get field(): FieldResult {
-        const { Document, fieldKey: fieldKey } = this.props;
-        return Document[fieldKey];
-    }
     render() {
-        const field = this.field;
+        const field = this.props.Document[this.props.fieldKey];
         // prettier-ignore
         if (field instanceof Doc)  return <p> <b>{field.title?.toString()}</b></p>;
         if (field === undefined) return <p>{'<null>'}</p>;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 5a8665aaf..cf07d98be 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -46,7 +46,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         return FieldView.LayoutString(FontIconBox, fieldKey);
     }
 
-    constructor(props: any) {
+    constructor(props: ButtonProps) {
         super(props);
         makeObservable(this);
     }
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index c26579e66..2e7a2120e 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -31,7 +31,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
     }
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         reaction(
             () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.layoutDoc.xRange, this.layoutDoc.yRange],
             () => this.createGraph()
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4483d12ce..2a10bd766 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -21,17 +21,17 @@ import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../../views/ContextMenu';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, OpenWhere } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
+import { OpenWhere } from './DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from './FieldView';
 import './ImageBox.scss';
 import { PinProps, PresBox } from './trails';
 
 @observer
-export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(ImageBox, fieldKey);
     }
@@ -55,10 +55,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private _marqueeref = React.createRef<MarqueeAnnotator>();
     @observable _curSuffix = '';
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -186,9 +186,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     crop = (region: Doc | undefined, addCrop?: boolean) => {
         if (!region) return;
         const cropping = Doc.MakeCopy(region, true);
-        Doc.GetProto(region).lockedPosition = true;
-        Doc.GetProto(region).title = 'region:' + this.Document.title;
-        Doc.GetProto(region).followLinkToggle = true;
+        const regionData = region[DocData];
+        regionData.lockedPosition = true;
+        regionData.title = 'region:' + this.Document.title;
+        regionData.followLinkToggle = true;
         this.addDocument(region);
         const anchx = NumCast(cropping.x);
         const anchy = NumCast(cropping.y);
@@ -201,7 +202,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping._width = anchw * (this._props.NativeDimScaling?.() || 1);
         cropping._height = anchh * (this._props.NativeDimScaling?.() || 1);
         cropping.onClick = undefined;
-        const croppingProto = Doc.GetProto(cropping);
+        const croppingProto = cropping[DocData];
         croppingProto.annotationOn = undefined;
         croppingProto.isDataDoc = true;
         croppingProto.backgroundColor = undefined;
@@ -389,7 +390,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._marqueeref.current?.onTerminateSelection();
         this._props.select(false);
     };
-    focus = (anchor: Doc, options: DocFocusOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options));
+    focus = (anchor: Doc, options: FocusViewOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options));
 
     _ffref = React.createRef<CollectionFreeFormView>();
     savedAnnotations = () => this._savedAnnotations;
@@ -421,7 +422,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 <CollectionFreeFormView
                     ref={this._ffref}
                     {...this._props}
-                    setContentView={emptyFunction}
+                    setContentViewBox={emptyFunction}
                     NativeWidth={returnZero}
                     NativeHeight={returnZero}
                     renderDepth={this._props.renderDepth + 1}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 73fdc3a23..39a45693e 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -44,7 +44,7 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> {
     private _valInput = React.createRef<HTMLInputElement>();
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
     isKeyValueBox = returnTrue;
     able = returnAlways;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index b2aab2422..7c532f33f 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -12,8 +12,7 @@ import { ContextMenu } from '../ContextMenu';
 import { EditableView } from '../EditableView';
 import { ObservableReactComponent } from '../ObservableReactComponent';
 import { DefaultStyleProvider } from '../StyleProvider';
-import { OpenWhere } from './DocumentView';
-import { FieldViewProps } from './FieldView';
+import { OpenWhere, returnEmptyDocViewList } from './DocumentView';
 import { KeyValueBox } from './KeyValueBox';
 import './KeyValueBox.scss';
 import './KeyValuePair.scss';
@@ -110,6 +109,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
                                 childFiltersByRanges: returnEmptyFilter,
                                 searchFilterDocs: returnEmptyDoclist,
                                 styleProvider: DefaultStyleProvider,
+                                docViewPath: returnEmptyDocViewList,
                                 fieldKey: this._props.keyName,
                                 isSelected: returnFalse,
                                 setHeight: returnFalse,
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index cc7c15a10..10eeff08d 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -14,13 +14,15 @@ import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
 import BigText from './LabelBigText';
 import './LabelBox.scss';
+import { PinProps, PresBox } from './trails';
+import { Docs } from '../../documents/Documents';
 
-export interface LabelBoxProps {
+export interface LabelBoxProps extends FieldViewProps {
     label?: string;
 }
 
 @observer
-export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProps>() {
+export class LabelBox extends ViewBoxBaseComponent<LabelBoxProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(LabelBox, fieldKey);
     }
@@ -30,13 +32,13 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: any;
 
-    constructor(props: any) {
+    constructor(props: LabelBoxProps) {
         super(props);
         makeObservable(this);
     }
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
     componentWillUnMount() {
         this._timeout && clearTimeout(this._timeout);
@@ -89,6 +91,19 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
         return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
     }
 
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+        if (!pinProps) return this.Document;
+        const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.Document.title), annotationOn: this.Document });
+
+        if (anchor) {
+            if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
+            //  addAsAnnotation && this.addDocument(anchor);
+            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}) } }, this.Document);
+            return anchor;
+        }
+        return anchor;
+    };
+
     fitTextToBox = (
         r: any
     ):
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 362a7def1..00e1f04c5 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,4 +1,5 @@
 import { action, computed, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
@@ -7,13 +8,14 @@ import { TraceMobx } from '../../../fields/util';
 import { DragManager } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
 import { SelectionManager } from '../../util/SelectionManager';
-import { ViewBoxBaseComponent, ViewBoxBaseProps } from '../DocComponent';
+import { ViewBoxBaseComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LinkAnchorBox.scss';
 import { LinkInfo } from './LinkDocPreview';
 const { default: { MEDIUM_GRAY }, } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
-export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps & ViewBoxBaseProps>() {
+@observer
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(LinkAnchorBox, fieldKey);
     }
@@ -23,13 +25,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps & ViewBox
     _isOpen = false;
     _timeout: NodeJS.Timeout | undefined;
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
 
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
 
     @computed get linkSource() {
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index ba34255dc..8b6293806 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -20,7 +20,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return FieldView.LayoutString(LinkBox, fieldKey);
     }
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -59,7 +59,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
     disposer: IReactionDisposer | undefined;
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         this.disposer = reaction(
             () => {
                 if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.CollectionFreeFormView) {
@@ -100,6 +100,9 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         this.layoutDoc._width = params.rx - params?.lx;
                         this.layoutDoc._height = params?.by - params?.ty;
                     }
+                } else {
+                    this.layoutDoc._width = Math.max(50, NumCast(this.layoutDoc._width));
+                    this.layoutDoc._height = Math.max(50, NumCast(this.layoutDoc._height));
                 }
             },
             { fireImmediately: true }
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index 8ad0b7dde..13f0ac4fc 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -1,20 +1,27 @@
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
 import { LinkManager } from '../../util/LinkManager';
 import './LinkDescriptionPopup.scss';
 import { TaskCompletionBox } from './TaskCompletedBox';
 
 @observer
 export class LinkDescriptionPopup extends React.Component<{}> {
-    @observable public static descriptionPopup: boolean = false;
-    @observable public static showDescriptions: string = 'ON';
-    @observable public static popupX: number = 700;
-    @observable public static popupY: number = 350;
+    public static Instance: LinkDescriptionPopup;
+    @observable public display: boolean = false;
+    @observable public showDescriptions: string = 'ON';
+    @observable public popupX: number = 700;
+    @observable public popupY: number = 350;
     @observable description: string = '';
     @observable popupRef = React.createRef<HTMLDivElement>();
 
+    constructor(props: any) {
+        super(props);
+        makeObservable(this);
+        LinkDescriptionPopup.Instance = this;
+    }
+
     @action
     descriptionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.description = e.currentTarget.value;
@@ -22,16 +29,16 @@ export class LinkDescriptionPopup extends React.Component<{}> {
 
     @action
     onDismiss = (add: boolean) => {
-        LinkDescriptionPopup.descriptionPopup = false;
+        this.display = false;
         if (add) {
-            LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).link_description = this.description);
+            LinkManager.currentLink && (LinkManager.currentLink[DocData].link_description = this.description);
         }
     };
 
     @action
     onClick = (e: PointerEvent) => {
         if (this.popupRef && !!!this.popupRef.current?.contains(e.target as any)) {
-            LinkDescriptionPopup.descriptionPopup = false;
+            this.display = false;
             TaskCompletionBox.taskCompleted = false;
         }
     };
@@ -46,13 +53,13 @@ export class LinkDescriptionPopup extends React.Component<{}> {
     }
 
     render() {
-        return (
+        return !this.display ? null : (
             <div
                 className="linkDescriptionPopup"
                 ref={this.popupRef}
                 style={{
-                    left: LinkDescriptionPopup.popupX ? LinkDescriptionPopup.popupX : 700,
-                    top: LinkDescriptionPopup.popupY ? LinkDescriptionPopup.popupY : 350,
+                    left: this.popupX ? this.popupX : 700,
+                    top: this.popupY ? this.popupY : 350,
                 }}>
                 <input
                     className="linkDescriptionPopup-input"
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index ed4878d9d..2d54da0ed 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -18,9 +18,9 @@ import { SearchUtil } from '../../util/SearchUtil';
 import { SettingsManager } from '../../util/SettingsManager';
 import { Transform } from '../../util/Transform';
 import { ObservableReactComponent } from '../ObservableReactComponent';
-import { DocumentView, DocumentViewSharedProps, OpenWhere, StyleProviderFunc } from './DocumentView';
+import { DocumentView, OpenWhere } from './DocumentView';
+import { StyleProviderFuncType } from './FieldView';
 import './LinkDocPreview.scss';
-import { FieldViewProps } from './FieldView';
 
 export class LinkInfo {
     private static _instance: Opt<LinkInfo>;
@@ -45,7 +45,7 @@ interface LinkDocPreviewProps {
     linkDoc?: Doc;
     linkSrc?: Doc;
     DocumentView?: () => DocumentView;
-    styleProvider?: StyleProviderFunc;
+    styleProvider?: StyleProviderFuncType;
     location: number[];
     hrefs?: string[];
     showHeader?: boolean;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 56fb157da..c185c66fc 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -23,12 +23,12 @@ import { DragManager } from '../../../util/DragManager';
 import { LinkManager } from '../../../util/LinkManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { UndoManager, undoable } from '../../../util/UndoManager';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { Colors } from '../../global/globalEnums';
-import { DocFocusOptions, DocumentView } from '../DocumentView';
-import { FieldView, FieldViewProps } from '../FieldView';
+import { DocumentView } from '../DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../trails';
 import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons';
@@ -96,7 +96,7 @@ type MapMarker = {
 // });
 
 @observer
-export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(MapBox, fieldKey);
     }
@@ -107,7 +107,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
@@ -248,7 +248,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     componentDidMount() {
         this._unmounting = false;
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
 
     _unmounting = false;
@@ -388,7 +388,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(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.Document;
+                        e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.Document;
                         e.annoDragData.linkSourceDoc.followLinkZoom = false;
                     }
                 },
@@ -504,7 +504,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
 
-    getView = async (doc: Doc, options: DocFocusOptions) => {
+    getView = async (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             this.toggleSidebar();
             options.didMove = true;
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 9734d9db1..9825824bd 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -12,7 +12,7 @@
 // import { SnappingManager } from '../../../util/SnappingManager';
 // import { UndoManager } from '../../../util/UndoManager';
 // import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
-// import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+// import { ViewBoxAnnotatableComponent } from '../../DocComponent';
 // import { Colors } from '../../global/globalEnums';
 // import { AnchorMenu } from '../../pdf/AnchorMenu';
 // import { Annotation } from '../../pdf/Annotation';
@@ -83,7 +83,7 @@
 // } as google.maps.places.AutocompleteOptions;
 
 // @observer
-// export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+// export class MapBox2 extends ViewBoxAnnotatableComponent<FieldViewProps & Partial<GoogleMapProps>>() {
 //     private _dropDisposer?: DragManager.DragDropDisposer;
 //     private _disposers: { [name: string]: IReactionDisposer } = {};
 //     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -137,7 +137,7 @@
 
 //     // iterate allMarkers to size, center, and zoom map to contain all markers
 //     private fitBounds = (map: google.maps.Map) => {
-//         const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
+//         const curBounds = map.getBounds ?? new window.google.maps.LatLngBounds();
 //         const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
 //         !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
 //     };
@@ -257,7 +257,7 @@
 //         map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
 //         map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
 //         setTimeout(() => {
-//             if (this._loadPending && this._map.getBounds()) {
+//             if (this._loadPending && this._map.getBounds) {
 //                 this._loadPending = false;
 //                 this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
 //             }
@@ -272,7 +272,7 @@
 
 //     @action
 //     centered = () => {
-//         if (this._loadPending && this._map.getBounds()) {
+//         if (this._loadPending && this._map.getBounds) {
 //             this._loadPending = false;
 //             this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
 //         }
@@ -282,7 +282,7 @@
 
 //     @action
 //     zoomChanged = () => {
-//         if (this._loadPending && this._map.getBounds()) {
+//         if (this._loadPending && this._map.getBounds) {
 //             this._loadPending = false;
 //             this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
 //         }
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index a9c6ba22c..6ccbbbe1c 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -9,19 +9,18 @@
 // import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 // import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
 // import { CollectionStackingView } from '../../collections/CollectionStackingView';
-// import { ViewBoxAnnotatableProps } from '../../DocComponent';
 // import { FieldViewProps } from '../FieldView';
 // import { FormattedTextBox } from '../formattedText/FormattedTextBox';
 // import './MapBox.scss';
 
-// interface MapBoxInfoWindowProps {
+// interface MapBoxInfoWindowProps extends FieldViewProps {
 //     place: Doc;
 //     renderDepth: number;
 //     markerMap: { [id: string]: google.maps.Marker };
 //     isAnyChildContentActive: () => boolean;
 // }
 // @observer
-// export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
+// export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps> {
 //     @action
 //     private handleInfoWindowClose = () => {
 //         if (this.props.place.infoWindowOpen) {
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index e079f7457..8a5bd7ce6 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Button, EditableText, IconButton, Type } from 'browndash-components';
-import { IReactionDisposer, ObservableMap, action, computed, observable, 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 { MapProvider, Map as MapboxMap } from 'react-map-gl';
@@ -16,18 +16,17 @@ import { LinkManager } from '../../../util/LinkManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { UndoManager, undoable } from '../../../util/UndoManager';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../../DocComponent';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { Colors } from '../../global/globalEnums';
-import { DocFocusOptions, DocumentView } from '../DocumentView';
-import { FieldView, FieldViewProps } from '../FieldView';
+import { DocumentView } from '../DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
 import { MapAnchorMenu } from '../MapBox/MapAnchorMenu';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../trails';
 import './MapBox.scss';
 
-// amongus
 /**
  * MapBox architecture:
  * Main component: MapBox.tsx
@@ -61,7 +60,7 @@ const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing
 // });
 
 @observer
-export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(MapBoxContainer, fieldKey);
     }
@@ -71,6 +70,11 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
     private _disposers: { [key: string]: IReactionDisposer } = {};
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
 
+    constructor(props: FieldViewProps) {
+        super(props);
+        makeObservable(this);
+    }
+
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
@@ -97,7 +101,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
 
     componentDidMount() {
         this._unmounting = false;
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
 
     _unmounting = false;
@@ -237,7 +241,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(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.Document;
+                        e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.Document;
                         e.annoDragData.linkSourceDoc.followLinkZoom = false;
                     }
                 },
@@ -374,7 +378,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
         }
     };
 
-    getView = async (doc: Doc, options: DocFocusOptions) => {
+    getView = async (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             this.toggleSidebar();
             options.didMove = true;
@@ -787,8 +791,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
                                           NativeWidth={returnOne}
                                           NativeHeight={returnOne}
                                           onKey={undefined}
-                                          onDoubleClick={undefined}
-                                          onBrowseClick={undefined}
+                                          onDoubleClickScript={undefined}
+                                          onBrowseClickScript={undefined}
                                           childFilters={returnEmptyFilter}
                                           childFiltersByRanges={returnEmptyFilter}
                                           searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 7696a45a0..0eb3ffc3d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -5,6 +5,7 @@ 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 { DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { ComputedField } from '../../../fields/ScriptField';
@@ -22,19 +23,19 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 import { CollectionStackingView } from '../collections/CollectionStackingView';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
 import { Colors } from '../global/globalEnums';
 import { CreateImage } from '../nodes/WebBoxRenderer';
 import { PDFViewer } from '../pdf/PDFViewer';
 import { SidebarAnnos } from '../SidebarAnnos';
-import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
+import { DocumentView, OpenWhere } from './DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from './FieldView';
 import { ImageBox } from './ImageBox';
 import './PDFBox.scss';
 import { PinProps, PresBox } from './trails';
 
 @observer
-export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(PDFBox, fieldKey);
     }
@@ -58,7 +59,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url;
     }
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
@@ -97,9 +98,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     crop = (region: Doc | undefined, addCrop?: boolean) => {
         if (!region) return;
         const cropping = Doc.MakeCopy(region, true);
-        Doc.GetProto(region).lockedPosition = true;
-        Doc.GetProto(region).title = 'region:' + this.Document.title;
-        Doc.GetProto(region).followLinkToggle = true;
+        const regionData = region[DocData];
+        regionData.lockedPosition = true;
+        regionData.title = 'region:' + this.Document.title;
+        regionData.followLinkToggle = true;
         this.addDocument(region);
 
         const docViewContent = this.DocumentView?.().ContentDiv!;
@@ -120,7 +122,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         cropping._width = anchw;
         cropping._height = anchh;
         cropping.onClick = undefined;
-        const croppingProto = Doc.GetProto(cropping);
+        const croppingProto = cropping[DocData];
         croppingProto.annotationOn = undefined;
         croppingProto.isDataDoc = true;
         croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
@@ -191,7 +193,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         this._disposers.select = reaction(
             () => this._props.isSelected(),
             () => {
@@ -219,12 +221,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         return this._props.addDocTab(doc, where);
     };
-    focus = (anchor: Doc, options: DocFocusOptions) => {
+    focus = (anchor: Doc, options: FocusViewOptions) => {
         this._initialScrollTarget = anchor;
         return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.config_scrollTop)), options);
     };
 
-    getView = async (doc: Doc, options: DocFocusOptions) => {
+    getView = async (doc: Doc, options: FocusViewOptions) => {
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
             options.didMove = true;
             this.toggleSidebar(false);
@@ -528,7 +530,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.DocumentView?.()!, false), true)}>
                     <ComponentTag
                         {...this._props}
-                        setContentView={emptyFunction} // override setContentView to do nothing
+                        setContentViewBox={emptyFunction} // override setContentView to do nothing
                         NativeWidth={this.sidebarNativeWidthFunc}
                         NativeHeight={this.sidebarNativeHeightFunc}
                         PanelHeight={this._props.PanelHeight}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index c04a81f7d..f6d94ce05 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -1,4 +1,4 @@
-import { action, observable } from 'mobx';
+import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DateField } from '../../../../fields/DateField';
@@ -21,6 +21,7 @@ import { media_state } from '../AudioBox';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { VideoBox } from '../VideoBox';
 import { RecordingView } from './RecordingView';
+import { DocData } from '../../../../fields/DocSymbols';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -30,8 +31,13 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
 
+    constructor(props: FieldViewProps) {
+        super(props);
+        makeObservable(this);
+    }
+
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         Doc.SetNativeWidth(this.dataDoc, 1280);
         Doc.SetNativeHeight(this.dataDoc, 720);
     }
@@ -100,7 +106,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
                   });
         screengrabber.overlayX = 70; //was -400
         screengrabber.overlayY = 590; //was 0
-        Doc.GetProto(screengrabber)[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
+        screengrabber[DocData][Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
         Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay
         DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
             RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index f74e6fb2b..1e3933ac3 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -21,12 +21,13 @@ import { TrackMovements } from '../../util/TrackMovements';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline';
 import { ContextMenu } from '../ContextMenu';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { media_state } from './AudioBox';
 import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import './ScreenshotBox.scss';
 import { VideoBox } from './VideoBox';
+import { DocData } from '../../../fields/DocSymbols';
 
 declare class MediaRecorder {
     constructor(e: any, options?: any); // whatever MediaRecorder has
@@ -109,7 +110,7 @@ declare class MediaRecorder {
 // }
 
 @observer
-export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(ScreenshotBox, fieldKey);
     }
@@ -121,7 +122,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         return Cast(this.dataDoc[this._props.fieldKey + '_recordingStart'], DateField)?.date.getTime();
     }
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         this.setupDictation();
@@ -144,7 +145,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     componentDidMount() {
         this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
-        this._props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+        this._props.setContentViewBox?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         // this.layoutDoc.videoWall && reaction(() => ({ width: this._props.PanelWidth(), height: this._props.PanelHeight() }),
         //     ({ width, height }) => {
         //         if (this._camera) {
@@ -281,7 +282,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.Document.x), NumCast(this.Document.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
         const textField = Doc.LayoutFieldKey(dictationText);
         dictationText._layout_autoHeight = false;
-        const dictationTextProto = Doc.GetProto(dictationText);
+        const dictationTextProto = dictationText[DocData];
         dictationTextProto[`${textField}_recordingSource`] = this.dataDoc;
         dictationTextProto[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
         dictationTextProto.mediaState = ComputedField.MakeFunction(`this.${textField}_recordingSource.mediaState`);
@@ -297,7 +298,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                     <div style={{ position: 'relative', height: this.videoPanelHeight() }}>
                         <CollectionFreeFormView
                             {...this._props}
-                            setContentView={emptyFunction}
+                            setContentViewBox={emptyFunction}
                             NativeWidth={returnZero}
                             NativeHeight={returnZero}
                             PanelHeight={this.videoPanelHeight}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 73ad3a004..89650889d 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -14,7 +14,7 @@ import { ScriptManager } from '../../util/ScriptManager';
 import { CompileScript, ScriptParam } from '../../util/Scripting';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { ContextMenu } from '../ContextMenu';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { EditableView } from '../EditableView';
 import { OverlayView } from '../OverlayView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
@@ -23,7 +23,7 @@ import './ScriptingBox.scss';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
-export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     private dropDisposer?: DragManager.DragDropDisposer;
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(ScriptingBox, fieldStr);
@@ -56,7 +56,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
     @observable private _scriptSuggestedParams: any = '';
     @observable private _scriptParamsText: any = '';
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         if (!this.compileParams.length) {
@@ -116,7 +116,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
     @action
     componentDidMount() {
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         this.rawText = this.rawScript;
         const observer = new _global.ResizeObserver(
             action((entries: any) => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 1913ab032..a6c524881 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -4,10 +4,11 @@ import { observer } from 'mobx-react';
 import { basename } from 'path';
 import * as React from 'react';
 import { Doc, Opt, StrListCast } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { AudioField, ImageField, VideoField } from '../../../fields/URLField';
 import { emptyFunction, formatTime, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -21,12 +22,12 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
 import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, DocumentView } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
+import { DocumentView } from './DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from './FieldView';
 import { RecordingBox } from './RecordingBox';
 import { PinProps, PresBox } from './trails';
 import './VideoBox.scss';
@@ -44,7 +45,7 @@ import './VideoBox.scss';
  */
 
 @observer
-export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(VideoBox, fieldKey);
     }
@@ -61,10 +62,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     private _playRegionTimer: any = null; // timeout for playback
     private _controlsFadeTimer: any = null; // timeout for controls fade
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
     }
 
     @observable _stackedTimeline: CollectionStackedTimeline | undefined = undefined; // CollectionStackedTimeline ref
@@ -115,7 +116,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     componentDidMount() {
         this.unmounting = false;
-        this._props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
+        this._props.setContentViewBox?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
         this.player && this.setPlayheadTime(this.timeline?.clipStart || 0);
         document.addEventListener('keydown', this.keyEvents, true);
 
@@ -329,11 +330,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             _height: (height / width) * 150,
             title: '--snapshot' + NumCast(this.layoutDoc._layout_currentTimecode) + ' image-',
         });
-        Doc.SetNativeWidth(Doc.GetProto(imageSnapshot), Doc.NativeWidth(this.layoutDoc));
-        Doc.SetNativeHeight(Doc.GetProto(imageSnapshot), Doc.NativeHeight(this.layoutDoc));
+        Doc.SetNativeWidth(imageSnapshot[DocData], Doc.NativeWidth(this.layoutDoc));
+        Doc.SetNativeHeight(imageSnapshot[DocData], Doc.NativeHeight(this.layoutDoc));
         this._props.addDocument?.(imageSnapshot);
         const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' });
-        link && (Doc.GetProto(link.link_anchor_2 as Doc).timecodeToHide = NumCast((link.link_anchor_2 as Doc).timecodeToShow) + 3);
+        link && (DocCast(link.link_anchor_2)[DocData].timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3);
         setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true));
     };
 
@@ -368,7 +369,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         !this.unmounting && this.player && (this.layoutDoc._layout_currentTimecode = this.player.currentTime);
     };
 
-    getView = (doc: Doc, options: DocFocusOptions) => {
+    getView = (doc: Doc, options: FocusViewOptions) => {
         if (this._stackedTimeline?.makeDocUnfiltered(doc)) {
             if (this.heightPercent === 100) {
                 this.layoutDoc._layout_timelineHeightPercent = VideoBox.heightPercent;
@@ -738,7 +739,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     zoom = (zoom: number) => this.timeline?.setZoom(zoom);
 
     // plays link
-    playLink = (doc: Doc, options: DocFocusOptions) => {
+    playLink = (doc: Doc, options: FocusViewOptions) => {
         const startTime = Math.max(0, NumCast(doc.config_clipStart, this._stackedTimeline?.anchorStart(doc) || 0));
         const endTime = this.timeline?.anchorEnd(doc);
         if (startTime !== undefined) {
@@ -885,10 +886,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     crop = (region: Doc | undefined, addCrop?: boolean) => {
         if (!region) return;
         const cropping = Doc.MakeCopy(region, true);
-        Doc.GetProto(region).backgroundColor = 'transparent';
-        Doc.GetProto(region).lockedPosition = true;
-        Doc.GetProto(region).title = 'region:' + this.Document.title;
-        Doc.GetProto(region).followLinkToggle = true;
+        const regionData = region[DocData];
+        regionData.backgroundColor = 'transparent';
+        regionData.lockedPosition = true;
+        regionData.title = 'region:' + this.Document.title;
+        regionData.followLinkToggle = true;
         region._timecodeToHide = NumCast(region._timecodeToShow) + 0.0001;
         this.addDocument(region);
         const anchx = NumCast(cropping.x);
@@ -904,7 +906,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.timecodeToHide = undefined;
         cropping.timecodeToShow = undefined;
         cropping.onClick = undefined;
-        const croppingProto = Doc.GetProto(cropping);
+        const croppingProto = cropping[DocData];
         croppingProto.annotationOn = undefined;
         croppingProto.isDataDoc = true;
         croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
@@ -955,7 +957,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         }}>
                         <CollectionFreeFormView
                             {...this._props}
-                            setContentView={emptyFunction}
+                            setContentViewBox={emptyFunction}
                             NativeWidth={returnZero}
                             NativeHeight={returnZero}
                             renderDepth={this._props.renderDepth + 1}
@@ -1004,7 +1006,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     @computed get UIButtons() {
-        const bounds = this.DocumentView?.().getBounds();
+        const bounds = this.DocumentView?.().getBounds;
         const width = (bounds?.right || 0) - (bounds?.left || 0);
         const curTime = NumCast(this.layoutDoc._layout_currentTimecode);
         return (
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index b62d32503..5776cf6a6 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -24,7 +24,7 @@ import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
 import { Colors } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
@@ -33,15 +33,15 @@ import { Annotation } from '../pdf/Annotation';
 import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
-import { DocComponentView, DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
-import { FieldView, FieldViewProps } from './FieldView';
+import { DocumentView, OpenWhere } from './DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from './FieldView';
 import { LinkInfo } from './LinkDocPreview';
 import { PinProps, PresBox } from './trails';
 import './WebBox.scss';
 const { CreateImage } = require('./WebBoxRenderer');
 const _global = (window /* browser */ || global) /* node */ as any;
 @observer
-export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(WebBox, fieldKey);
     }
@@ -98,7 +98,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return Cast(this.Document[this._props.fieldKey], WebField)?.url;
     }
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         this._webUrl = this._url; // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
@@ -172,7 +172,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     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)
+        this._props.setContentViewBox?.(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';
@@ -280,7 +280,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._savedAnnotations;
     };
 
-    focus = (anchor: Doc, options: DocFocusOptions) => {
+    focus = (anchor: Doc, options: FocusViewOptions) => {
         if (anchor !== this.Document && this._outerRef.current) {
             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));
@@ -297,7 +297,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     @action
-    getView = (doc: Doc, options: DocFocusOptions) => {
+    getView = (doc: Doc, options: FocusViewOptions) => {
         if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this.DocumentView?.()));
         if (this.Document.layout_fieldKey === 'layout_icon') this.DocumentView?.().iconify();
         const webUrl = WebCast(doc.config_data)?.url;
@@ -924,7 +924,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     _innerCollectionView: CollectionFreeFormView | undefined;
     zoomScaling = () => this._innerCollectionView?.zoomScaling() ?? 1;
-    setInnerContent = (component: DocComponentView) => (this._innerCollectionView = component as CollectionFreeFormView);
+    setInnerContent = (component: ViewBoxInterface) => (this._innerCollectionView = component as CollectionFreeFormView);
 
     @computed get content() {
         const interactive = this._props.isContentActive() && this._props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
@@ -966,7 +966,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     renderAnnotations = (childFilters: () => string[]) => (
         <CollectionFreeFormView
             {...this._props}
-            setContentView={this.setInnerContent}
+            setContentViewBox={this.setInnerContent}
             NativeWidth={returnZero}
             NativeHeight={returnZero}
             originTopLeft={false}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 4fb1a7f17..748c3322e 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -18,6 +18,11 @@ export class CalendarBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return FieldView.LayoutString(CalendarBox, fieldKey);
     }
 
+    constructor(props: FieldViewProps) {
+        super(props);
+        makeObservable(this);
+    }
+
     componentDidMount(): void {}
 
     componentWillUnmount(): void {}
@@ -105,11 +110,6 @@ export class CalendarBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
     }
 
-    constructor(props: any) {
-        super(props);
-        makeObservable(this);
-    }
-
     render() {
         return (
             <div className="calendar-box-conatiner">
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 4ae12505e..bc49ed23d 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -6,12 +6,13 @@ import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
 import { Height, Width } from '../../../../fields/DocSymbols';
 import { NumCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnFalse, Utils } from '../../../../Utils';
+import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { Transform } from '../../../util/Transform';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
-import { DocFocusOptions, DocumentView, returnEmptyDocViewList } from '../DocumentView';
+import { DocumentView } from '../DocumentView';
+import { FocusViewOptions } from '../FieldView';
 import { FormattedTextBox } from './FormattedTextBox';
 
 var horizPadding = 3; // horizontal padding to container to allow cursor to show up on either side.
@@ -159,7 +160,7 @@ export class DashDocViewInternal extends ObservableReactComponent<IDashDocViewIn
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._spanRef.current);
         return new Transform(-translateX, -translateY, 1).scale(1 / scale);
     };
-    outerFocus = (target: Doc, options: DocFocusOptions) => this._textBox.focus(target, options); // ideally, this would scroll to show the focus target
+    outerFocus = (target: Doc, options: FocusViewOptions) => this._textBox.focus(target, options); // ideally, this would scroll to show the focus target
 
     onKeyDown = (e: any) => {
         e.stopPropagation();
@@ -178,9 +179,6 @@ export class DashDocViewInternal extends ObservableReactComponent<IDashDocViewIn
         ele && (ele.style.backgroundColor = 'orange');
     };
 
-    @computed get childContainerViewPath() {
-        return this._textBox.DocumentView?.().docViewPath ?? returnEmptyDocViewList;
-    }
     componentWillUnmount = () => Object.values(this._disposers).forEach(disposer => disposer?.());
     isContentActive = () => this._props.tbox._props.isContentActive() || this._props.tbox.isAnyChildContentActive?.();
 
@@ -210,7 +208,7 @@ export class DashDocViewInternal extends ObservableReactComponent<IDashDocViewIn
                     isDocumentActive={returnFalse}
                     isContentActive={this.isContentActive}
                     styleProvider={this._textBox._props.styleProvider}
-                    containerViewPath={this.childContainerViewPath}
+                    containerViewPath={this._textBox.DocumentView?.().docViewPath}
                     ScreenToLocalTransform={this.getDocTransform}
                     addDocTab={this._textBox._props.addDocTab}
                     pinToPres={returnFalse}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 65898d568..38400d2e7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -14,7 +14,7 @@ import * as React from 'react';
 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, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -42,7 +42,7 @@ import { CollectionStackingView } from '../../collections/CollectionStackingView
 import { CollectionTreeView } from '../../collections/CollectionTreeView';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { LightboxView } from '../../LightboxView';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
@@ -50,8 +50,8 @@ import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { StyleProp } from '../../StyleProvider';
 import { media_state } from '../AudioBox';
-import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
-import { FieldView, FieldViewProps } from '../FieldView';
+import { DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
 import { LinkInfo } from '../LinkDocPreview';
 import { PinProps, PresBox } from '../trails';
 import { DashDocCommentView } from './DashDocCommentView';
@@ -68,10 +68,8 @@ import { RichTextRules } from './RichTextRules';
 import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
 // import * as applyDevTools from 'prosemirror-dev-tools';
-
-export interface FormattedTextBoxProps {}
 @observer
-export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & FormattedTextBoxProps>() {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
@@ -202,7 +200,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
         return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docId
     }
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         FormattedTextBox.Instance = this;
@@ -271,14 +269,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
                 if (target) {
                     anchor.followLinkAudio = true;
                     let stopFunc: any;
-                    Doc.GetProto(target).mediaState = media_state.Recording;
-                    Doc.GetProto(target).audioAnnoState = 'recording';
-                    DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target), stop => (stopFunc = stop));
+                    const targetData = target[DocData];
+                    targetData.mediaState = media_state.Recording;
+                    targetData.audioAnnoState = 'recording';
+                    DocumentViewInternal.recordAudioAnnotation(targetData, Doc.LayoutFieldKey(target), stop => (stopFunc = stop));
                     let reactionDisposer = reaction(
                         () => target.mediaState,
                         action(dictation => {
                             if (!dictation) {
-                                Doc.GetProto(target).audioAnnoState = 'stopped';
+                                targetData.audioAnnoState = 'stopped';
                                 stopFunc();
                                 reactionDisposer();
                             }
@@ -433,7 +432,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
 
     autoLink = () => {
         const newAutoLinks = new Set<Doc>();
-        const oldAutoLinks = LinkManager.Links(this._props.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords);
+        const oldAutoLinks = LinkManager.Links(this.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords);
         if (this._editorView?.state.doc.textContent) {
             const isNodeSel = this._editorView.state.selection instanceof NodeSelection;
             const f = this._editorView.state.selection.from;
@@ -451,7 +450,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
     updateTitle = () => {
         const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text);
         if (
-            !this._props.dontRegisterView && // (this._props.Document.isTemplateForField === "text" || !this._props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+            !this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
             (title.startsWith('-') || title.startsWith('@')) &&
             this._editorView &&
             !this.dataDoc.title_custom &&
@@ -494,7 +493,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
                                     DocUtils.MakeLink(this.Document, target, { link_relationship: LinkManager.AutoKeywords })!);
                             newAutoLinks.add(alink);
                             // DocCast(alink.link_anchor_1).followLinkLocation = 'add:right';
-                            const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this._props.Document[Id] }];
+                            const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.Document[Id] }];
                             allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
                             const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term' });
                             tr = tr.addMark(pos, pos + node.nodeSize, link);
@@ -578,7 +577,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
                 // replace text contents when dragging with Alt
                 if (de.altKey) {
                     const fieldKey = Doc.LayoutFieldKey(draggedDoc);
-                    if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this._props.Document)) {
+                    if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.Document)) {
                         Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]);
                     }
 
@@ -991,7 +990,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
                 };
                 const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement();
                 if (link) {
-                    Doc.GetProto(link).isDictation = true;
+                    link[DocData].isDictation = true;
                     const audioanchor = Cast(link.link_anchor_2, Doc, null);
                     const textanchor = Cast(link.link_anchor_1, Doc, null);
                     if (audioanchor) {
@@ -1001,7 +1000,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
                             audioId: audioanchor[Id],
                             textId: textanchor[Id],
                         });
-                        Doc.GetProto(textanchor).title = 'dictation:' + audiotag.attrs.timeCode;
+                        textanchor[DocData].title = 'dictation:' + audiotag.attrs.timeCode;
                         const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag);
                         const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
                         this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
@@ -1056,7 +1055,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
         return anchorDoc ?? this.Document;
     }
 
-    getView = async (doc: Doc, options: DocFocusOptions) => {
+    getView = async (doc: Doc, options: FocusViewOptions) => {
         if (DocListCast(this.dataDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
             if (!this.SidebarShown) {
                 this.toggleSidebar(false);
@@ -1066,7 +1065,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
         }
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
     };
-    focus = (textAnchor: Doc, options: DocFocusOptions) => {
+    focus = (textAnchor: Doc, options: FocusViewOptions) => {
         const focusSpeed = options.zoomTime ?? 500;
         const textAnchorId = textAnchor[Id];
         const findAnchorFrag = (frag: Fragment, editor: EditorView) => {
@@ -1141,7 +1140,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
         return Doc.NativeAspect(this.Document, this.dataDoc, false) ? this._props.NativeDimScaling?.() || 1 : 1;
     }
     componentDidMount() {
-        !this._props.dontSelectOnLoad && this._props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+        !this._props.dontSelectOnLoad && this._props.setContentViewBox?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this._cachedLinks = LinkManager.Links(this.Document);
         this._disposers.breakupDictation = reaction(() => Doc.RecordingEvent, this.breakupDictation);
         this._disposers.layout_autoHeight = reaction(
@@ -1777,7 +1776,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
             }
         }
     };
-    fitContentsToBox = () => BoolCast(this._props.Document._freeform_fitContentsToBox);
+    fitContentsToBox = () => BoolCast(this.Document._freeform_fitContentsToBox);
     sidebarContentScaling = () => (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => {
         if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
@@ -2011,7 +2010,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotat
                         cursor: this._props.isContentActive() ? 'text' : undefined,
                         height: this._props.height ? 'max-content' : undefined,
                         overflow: this.layout_autoHeight ? 'hidden' : undefined,
-                        pointerEvents: Doc.ActiveTool === InkTool.None && !this._props.onBrowseClick?.() ? undefined : 'none',
+                        pointerEvents: Doc.ActiveTool === InkTool.None && !this._props.onBrowseClickScript?.() ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
                     onKeyDown={this.onKeyDown}
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index 7d0086c0c..6e7c3e612 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -1,4 +1,4 @@
-import { computed } from 'mobx';
+import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { returnFalse } from '../../../../Utils';
@@ -12,6 +12,10 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(ImportElementBox, fieldKey);
     }
+    constructor(props: FieldViewProps) {
+        super(props);
+        makeObservable(this);
+    }
 
     screenToLocalXf = () => this.ScreenToLocalBoxXf().scale(1 * (this._props.NativeDimScaling?.() || 1));
     @computed get mainItem() {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 645ac08e1..9e5ea9524 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, makeObservable, observable, Observ
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, FieldResult, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
-import { Animation } from '../../../../fields/DocSymbols';
+import { Animation, DocData } from '../../../../fields/DocSymbols';
 import { Copy, Id } from '../../../../fields/FieldSymbols';
 import { InkField } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -31,8 +31,8 @@ import { TreeView } from '../../collections/TreeView';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { LightboxView } from '../../LightboxView';
-import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView';
-import { FieldView, FieldViewProps } from '../FieldView';
+import { DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView';
+import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
 import { ScriptingBox } from '../ScriptingBox';
 import './PresBox.scss';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
@@ -70,7 +70,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     static navigateToDocScript: ScriptField;
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
         if (!PresBox.navigateToDocScript) {
@@ -184,7 +184,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             },
             { fireImmediately: true }
         );
-        this._props.setContentView?.(this);
+        this._props.setContentViewBox?.(this);
         this._unmounting = false;
         this.turnOffEdit(true);
         this._disposers.selection = reaction(
@@ -438,9 +438,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const setData = bestTargetView?.ComponentView?.setData;
             if (setData) setData(activeItem.config_data);
             else {
-                const current = Doc.GetProto(bestTarget)[fkey];
-                Doc.GetProto(bestTarget)[fkey + '_' + Date.now()] = current instanceof ObjectField ? current[Copy]() : current;
-                Doc.GetProto(bestTarget)[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+                const bestTargetData = bestTarget[DocData];
+                const current = bestTargetData[fkey];
+                bestTargetData[fkey + '_' + Date.now()] = current instanceof ObjectField ? current[Copy]() : current;
+                bestTargetData[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
             }
             bestTarget[fkey + '_usePath'] = activeItem.config_usePath;
             setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
@@ -498,11 +499,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.config_fillColor !== undefined || activeItem.color !== undefined))) {
             if (bestTarget.fillColor !== activeItem.config_fillColor) {
-                Doc.GetProto(bestTarget).fillColor = StrCast(activeItem.config_fillColor, StrCast(bestTarget.fillColor));
+                bestTarget[DocData].fillColor = StrCast(activeItem.config_fillColor, StrCast(bestTarget.fillColor));
                 changed = true;
             }
             if (bestTarget.color !== activeItem.config_color) {
-                Doc.GetProto(bestTarget).color = StrCast(activeItem.config_color, StrCast(bestTarget.color));
+                bestTarget[DocData].color = StrCast(activeItem.config_color, StrCast(bestTarget.color));
                 changed = true;
             }
             if (bestTarget.width !== activeItem.width) {
@@ -559,7 +560,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     return doc;
                 });
             const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
-            Doc.GetProto(bestTarget)[fkey + '_annotations'] = newList;
+            bestTarget[DocData][fkey + '_annotations'] = newList;
         }
         if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.config_pinLayoutData !== undefined)) {
             changed = true;
@@ -580,9 +581,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         data.fill && (doc._fillColor = data.fill);
                         doc._width = data.w;
                         doc._height = data.h;
-                        data.data && (Doc.GetProto(doc).data = field);
-                        data.text && (Doc.GetProto(doc).text = tfield);
-                        Doc.AddDocToList(Doc.GetProto(bestTarget), layoutField, doc);
+                        data.data && (doc[DocData].data = field);
+                        data.text && (doc[DocData].text = tfield);
+                        Doc.AddDocToList(bestTarget[DocData], layoutField, doc);
                     }
                 });
             setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10);
@@ -648,7 +649,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             if (pinProps.pinData.dataannos) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
-                pinDoc.config_annotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '_annotations']).filter(doc => !doc.layout_unrendered));
+                pinDoc.config_annotations = new List<Doc>(DocListCast(targetDoc[DocData][fkey + '_annotations']).filter(doc => !doc.layout_unrendered));
             }
             if (pinProps.pinData.inkable) {
                 pinDoc.config_fillColor = targetDoc.fillColor;
@@ -763,7 +764,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         const effect = activeItem.presentation_effect && activeItem.presentation_effect !== PresEffect.None ? activeItem.presentation_effect : undefined;
         const presTime = NumCast(activeItem.presentation_transition, effect ? 750 : 500);
-        const options: DocFocusOptions = {
+        const options: FocusViewOptions = {
             willPan: activeItem.presentation_movement !== PresMovement.None,
             willZoomCentered: activeItem.presentation_movement === PresMovement.Zoom || activeItem.presentation_movement === PresMovement.Jump || activeItem.presentation_movement === PresMovement.Center,
             zoomScale: activeItem.presentation_movement === PresMovement.Center ? 0 : NumCast(activeItem.config_zoom, 1),
@@ -1115,7 +1116,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         presDocView && SelectionManager.SelectView(presDocView, false);
     };
 
-    focusElement = (doc: Doc, options: DocFocusOptions) => {
+    focusElement = (doc: Doc, options: FocusViewOptions) => {
         this.selectElement(doc);
         return undefined;
     };
@@ -2605,7 +2606,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
                                 childDragAction="move"
-                                setContentView={emptyFunction}
+                                setContentViewBox={emptyFunction}
                                 //childLayoutFitWidth={returnTrue}
                                 childOpacity={returnOne}
                                 childClickScript={PresBox.navigateToDocScript}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 4bc79176e..7e68711fa 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -41,7 +41,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     readonly expandViewHeight = 100;
     readonly collapsedHeight = 35;
 
-    constructor(props: any) {
+    constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 01590749e..ba7286111 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -17,16 +17,15 @@ import { SnappingManager } from '../../util/SnappingManager';
 import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
-import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
 import { LinkInfo } from '../nodes/LinkDocPreview';
+import { PDFBox } from '../nodes/PDFBox';
 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 { PDFBox } from '../nodes/PDFBox';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
@@ -172,7 +171,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
 
     // scrolls to focus on a nested annotation document.  if this is part a link preview then it will jump to the scroll location,
     // otherwise it will scroll smoothly.
-    scrollFocus = (doc: Doc, scrollTop: number, options: DocFocusOptions) => {
+    scrollFocus = (doc: Doc, scrollTop: number, options: FocusViewOptions) => {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
         if (doc !== this._props.Document && mainCont) {
@@ -521,7 +520,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
                 {...this._props}
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
-                setContentView={emptyFunction} // override setContentView to do nothing
+                setContentViewBox={emptyFunction} // override setContentView to do nothing
                 pointerEvents={this._props.isContentActive() && (SnappingManager.IsDragging || Doc.ActiveTool !== InkTool.None) ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it.
                 childPointerEvents={this.childPointerEvents} // but freeform children need to get events to allow text editing, etc
                 renderDepth={this._props.renderDepth + 1}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 4d29573d4..5dc4f5550 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -3,7 +3,7 @@ import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCastAsync, Field } from '../../../fields/Doc';
-import { DirectLinks } from '../../../fields/DocSymbols';
+import { DirectLinks, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { DocCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -58,7 +58,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     /**
      * This is the constructor for the SearchBox class.
      */
-    constructor(props: any) {
+    constructor(props: SearchBoxProps) {
         super(props);
         makeObservable(this);
         SearchBox.Instance = this;
@@ -207,7 +207,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         this._results.forEach((_, doc) => {
             this._pageRanks.set(doc, 1.0 / this._results.size);
 
-            if (Doc.GetProto(doc)[DirectLinks].size === 0) {
+            if (doc[DocData][DirectLinks].size === 0) {
                 this._linkedDocsOut.set(doc, new Set(this._results.keys()));
 
                 this._results.forEach((_, linkedDoc) => {
@@ -216,7 +216,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             } else {
                 const linkedDocSet = new Set<Doc>();
 
-                Doc.GetProto(doc)[DirectLinks].forEach(link => {
+                doc[DocData][DirectLinks].forEach(link => {
                     const d1 = link?.link_anchor_1 as Doc;
                     const d2 = link?.link_anchor_2 as Doc;
                     if (doc === d1 && this._results.has(d2)) {
diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx
index 39e778b76..c9c01189e 100644
--- a/src/client/views/selectedDoc/SelectedDocView.tsx
+++ b/src/client/views/selectedDoc/SelectedDocView.tsx
@@ -1,14 +1,14 @@
-import * as React from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { ListBox } from 'browndash-components';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { StrCast } from '../../../fields/Types';
 import { DocumentManager } from '../../util/DocumentManager';
-import { DocFocusOptions } from '../nodes/DocumentView';
-import { emptyFunction } from '../../../Utils';
 import { SettingsManager } from '../../util/SettingsManager';
+import { FocusViewOptions } from '../nodes/FieldView';
 
 export interface SelectedDocViewProps {
     selectedDocs: Doc[];
@@ -25,7 +25,7 @@ export class SelectedDocView extends React.Component<SelectedDocViewProps> {
             <div className="selectedDocView-container">
                 <ListBox
                     items={this.selectedDocs.map(doc => {
-                        const options: DocFocusOptions = {
+                        const options: FocusViewOptions = {
                             playAudio: false,
                             playMedia: false,
                             willPan: true,
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 94458563e..4e984f3d6 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -8,7 +8,6 @@ import { Doc } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
 import { SnappingManager } from '../../util/SnappingManager';
 import '../../views/nodes/WebBox.scss';
-import { CollectionFreeFormDocumentViewProps } from '../nodes/CollectionFreeFormDocumentView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import './DashWebRTCVideo.scss';
 import { hangup, initialize, refreshVideos } from './WebCamLogic';
@@ -17,14 +16,12 @@ import { hangup, initialize, refreshVideos } from './WebCamLogic';
  * This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
  */
 @observer
-export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentViewProps & FieldViewProps> {
+export class DashWebRTCVideo extends React.Component<FieldViewProps> {
     private roomText: HTMLInputElement | undefined;
     @observable remoteVideoAdded: boolean = false;
 
     @action
-    changeUILook = () => {
-        this.remoteVideoAdded = true;
-    };
+    changeUILook = () => (this.remoteVideoAdded = true);
 
     /**
      * Function that submits the title entered by user on enter press.
@@ -42,14 +39,9 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
         return FieldView.LayoutString(DashWebRTCVideo, fieldKey);
     }
 
-    @action
-    onClickRefresh = () => {
-        refreshVideos();
-    };
+    onClickRefresh = () => refreshVideos();
 
-    onClickHangUp = () => {
-        hangup();
-    };
+    onClickHangUp = () => hangup();
 
     render() {
         const content = (
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9c2350bd0..ff416bbe7 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -31,6 +31,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor }
 import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
 import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
 import * as JSZip from 'jszip';
+import { FieldViewProps } from '../client/views/nodes/FieldView';
 export const LinkedTo = '-linkedTo';
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -423,7 +424,7 @@ export namespace Doc {
     //
     export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
         if (key.startsWith('_')) key = key.substring(1);
-        const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined;
+        const hasProto = doc[DocData] !== doc ? doc[DocData] : undefined;
         const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
         const onProto = hasProto && Object.getOwnPropertyNames(hasProto).indexOf(key) !== -1;
         if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
@@ -510,7 +511,7 @@ export namespace Doc {
     export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc) {
         const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
         if (listDoc[key] === undefined) {
-            Doc.GetProto(listDoc)[key] = new List<Doc>();
+            listDoc[DocData][key] = new List<Doc>();
         }
         const list = Cast(listDoc[key], listSpec(Doc));
         if (list) {
@@ -530,7 +531,7 @@ export namespace Doc {
     export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) {
         const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc);
         if (listDoc[key] === undefined) {
-            Doc.GetProto(listDoc)[key] = new List<Doc>();
+            listDoc[DocData][key] = new List<Doc>();
         }
         const list = Cast(listDoc[key], listSpec(Doc));
         if (list) {
@@ -564,7 +565,7 @@ export namespace Doc {
             Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
         }
         embedding.createdFrom = doc;
-        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length - 1;
+        embedding.proto_embeddingId = doc[DocData].proto_embeddingId = DocListCast(doc[DocData].proto_embeddings).length - 1;
         !Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`));
         embedding.author = Doc.CurrentUserEmail;
 
@@ -1084,8 +1085,8 @@ export namespace Doc {
     export function LayoutField(doc: Doc) {
         return doc[StrCast(doc.layout_fieldKey, 'layout')];
     }
-    export function LayoutFieldKey(doc: Doc): string {
-        return StrCast(Doc.Layout(doc).layout).split("'")[1]; // bcz: TODO check on this .  used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
+    export function LayoutFieldKey(doc: Doc, templateLayoutString?: string): string {
+        return StrCast(templateLayoutString || Doc.Layout(doc).layout).split("'")[1]; // bcz: TODO check on this .  used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
     }
     export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
         return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
@@ -1100,10 +1101,10 @@ export namespace Doc {
         return NumCast(doc._nativeHeight, nheight || dheight);
     }
     export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) {
-        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeWidth'] = width;
+        doc[(fieldKey || Doc.LayoutFieldKey(doc)) + '_nativeWidth'] = width;
     }
     export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) {
-        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
+        doc[(fieldKey || Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
     }
 
     const manager = new UserDocData();
@@ -1127,22 +1128,22 @@ export namespace Doc {
     }
 
     const isSearchMatchCache = computedFn(function IsSearchMatch(doc: Doc) {
-        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : brushManager.SearchMatchDoc.has(doc[DocData]) ? brushManager.SearchMatchDoc.get(doc[DocData]) : undefined;
     });
     export function IsSearchMatch(doc: Doc) {
         return isSearchMatchCache(doc);
     }
     export function IsSearchMatchUnmemoized(doc: Doc) {
-        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : brushManager.SearchMatchDoc.has(doc[DocData]) ? brushManager.SearchMatchDoc.get(doc[DocData]) : undefined;
     }
     export function SetSearchMatch(doc: Doc, results: { searchMatch: number }) {
-        if (doc && GetEffectiveAcl(doc) !== AclPrivate && GetEffectiveAcl(Doc.GetProto(doc)) !== AclPrivate) {
+        if (doc && GetEffectiveAcl(doc) !== AclPrivate && GetEffectiveAcl(doc[DocData]) !== AclPrivate) {
             brushManager.SearchMatchDoc.set(doc, results);
         }
         return doc;
     }
     export function SearchMatchNext(doc: Doc, backward: boolean) {
-        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(doc[DocData]) === AclPrivate) return doc;
         const result = brushManager.SearchMatchDoc.get(doc);
         const num = Math.abs(result?.searchMatch || 0) + 1;
         runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num }));
@@ -1160,13 +1161,13 @@ export namespace Doc {
     }
     // returns 'how' a Doc has been brushed over - whether the document itself was brushed, it's prototype, or neither
     export function GetBrushStatus(doc: Doc) {
-        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
-        return doc[Brushed] ? DocBrushStatus.selfBrushed : Doc.GetProto(doc)[Brushed] ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(doc[DocData]) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
+        return doc[Brushed] ? DocBrushStatus.selfBrushed : doc[DocData][Brushed] ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
     }
     export function BrushDoc(doc: Doc, unbrush = false) {
-        if (doc && GetEffectiveAcl(doc) !== AclPrivate && GetEffectiveAcl(Doc.GetProto(doc)) !== AclPrivate) {
+        if (doc && GetEffectiveAcl(doc) !== AclPrivate && GetEffectiveAcl(doc[DocData]) !== AclPrivate) {
             brushManager.brushDoc(doc, unbrush);
-            brushManager.brushDoc(Doc.GetProto(doc), unbrush);
+            brushManager.brushDoc(doc[DocData], unbrush);
         }
         return doc;
     }
@@ -1186,6 +1187,7 @@ export namespace Doc {
     }
     export function linkFollowUnhighlight() {
         clearTimeout(UnhighlightTimer);
+        UnhighlightTimer = 0;
         UnhighlightWatchers.forEach(watcher => watcher());
         UnhighlightWatchers.length = 0;
         highlightedDocs.forEach(doc => Doc.UnHighlightDoc(doc));
@@ -1205,8 +1207,8 @@ export namespace Doc {
 
     export var highlightedDocs = new ObservableSet<Doc>();
     export function IsHighlighted(doc: Doc) {
-        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false;
-        return doc[Highlight] || Doc.GetProto(doc)[Highlight];
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(doc[DocData]) === AclPrivate || doc.opacity === 0) return false;
+        return doc[Highlight] || doc[DocData][Highlight];
     }
     export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presentation_effect?: Doc) {
         runInAction(() => {
@@ -1214,8 +1216,8 @@ export namespace Doc {
             doc[Highlight] = true;
             doc[Animation] = presentation_effect;
             if (dataAndDisplayDocs) {
-                highlightedDocs.add(Doc.GetProto(doc));
-                Doc.GetProto(doc)[Highlight] = true;
+                highlightedDocs.add(doc[DocData]);
+                doc[DocData][Highlight] = true;
             }
         });
     }
@@ -1224,8 +1226,8 @@ export namespace Doc {
         runInAction(() => {
             (doc ? [doc] : Array.from(highlightedDocs)).forEach(doc => {
                 highlightedDocs.delete(doc);
-                highlightedDocs.delete(Doc.GetProto(doc));
-                doc[Highlight] = Doc.GetProto(doc)[Highlight] = false;
+                highlightedDocs.delete(doc[DocData]);
+                doc[Highlight] = doc[DocData][Highlight] = false;
                 doc[Animation] = undefined;
             });
         });
@@ -1346,7 +1348,7 @@ export namespace Doc {
         });
     }
 
-    export function styleFromLayoutString(doc: Doc, props: any, scale: number) {
+    export function styleFromLayoutString(doc: Doc, props: FieldViewProps, 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) => {
@@ -1354,7 +1356,7 @@ export namespace Doc {
             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];
+            const p = (props as any)[prop];
             typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
         });
         return style;
@@ -1593,7 +1595,7 @@ ScriptingGlobals.add(function idToDoc(id: string): any {
     return IdToDoc(id);
 });
 ScriptingGlobals.add(function renameEmbedding(doc: any) {
-    return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
+    return StrCast(doc[DocData].title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
 });
 ScriptingGlobals.add(function getProto(doc: any) {
     return Doc.GetProto(doc);
-- 
cgit v1.2.3-70-g09d2

