From 19aa61f593c896f4d3bd0ffae6dbe306c2a9546d Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Wed, 10 Aug 2022 11:51:41 -0400
Subject: basic table in progress

---
 .../collections/collectionSchema/SchemaRowBox.tsx  | 33 ++++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 src/client/views/collections/collectionSchema/SchemaRowBox.tsx

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

diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
new file mode 100644
index 000000000..50e2502dc
--- /dev/null
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -0,0 +1,33 @@
+import React = require('react');
+import { observer } from 'mobx-react';
+import './CollectionSchemaView.scss';
+import { ViewBoxAnnotatableComponent, ViewBoxBaseComponent } from '../../DocComponent';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { SchemaTableCell } from './SchemaTableCell';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { undoBatch } from '../../../util/UndoManager';
+
+export interface SchemaRowBoxProps extends FieldViewProps {
+    columnKeys: string[];
+    columnWidths: number[];
+}
+
+@observer
+export class SchemaRowBox extends ViewBoxAnnotatableComponent<SchemaRowBoxProps>() {
+    render() {
+        return (
+            <div className="schema-row" /*style={this.props.isSelected() ? { backgroundColor: 'grey' } : {}}*/>
+                <div className="row-menu">
+                    <div className="row-button" onPointerDown={undoBatch(() => this.props.removeDocument?.(this.props.Document))}>
+                        <FontAwesomeIcon icon={'times'} />
+                    </div>
+                </div>
+                <div className="row-cells">
+                    {this.props.columnKeys.map((key, index) => (
+                        <SchemaTableCell Document={this.props.Document} key={key} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+                    ))}
+                </div>
+            </div>
+        );
+    }
+}
-- 
cgit v1.2.3-70-g09d2


From 9f39b14b2e321e09d071ad1c5bf8e3978ff7f181 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Wed, 10 Aug 2022 19:33:19 -0400
Subject: basic selection complete

---
 .../collections/CollectionStackedTimeline.scss     |  1 +
 .../collections/CollectionStackedTimeline.tsx      |  2 +-
 .../collectionSchema/CollectionSchemaView.scss     |  1 -
 .../collectionSchema/CollectionSchemaView.tsx      | 57 ++++++++++++++--------
 .../collections/collectionSchema/SchemaRowBox.tsx  | 35 +++++++++----
 src/client/views/nodes/AudioBox.tsx                |  1 +
 6 files changed, 65 insertions(+), 32 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index 6611477e5..c296e1172 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -2,6 +2,7 @@
 
 .collectionStackedTimeline-timelineContainer {
     height: 100%;
+    position: absolute;
     overflow-x: auto;
     overflow-y: hidden;
     border: none;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 48e3abbc7..2543624d3 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -668,7 +668,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         )}
                     </div>
                 </div>
-                <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
+                <div className="timeline-hoverUI" style={{ left: ((this._hoverTime - this.clipStart) / this.clipDuration) * this.timelineContentWidth - this._scroll }}>
                     <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
                     {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
                 </div>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 0a51aea4e..0f4053127 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -36,7 +36,6 @@
                 display: flex;
                 flex-direction: row;
                 justify-content: center;
-                width: 100px;
 
                 .row-button {
                     width: 20px;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 7e903ca92..20d809232 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,15 +1,14 @@
 import React = require('react');
+import { action, computed, observable, ObservableMap, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast } from '../../../../fields/Types';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
-import { SchemaRowBox } from './SchemaRowBox';
-import { action, computed, observable } from 'mobx';
-import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
-import { listSpec } from '../../../../fields/Schema';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
-import { List } from '../../../../fields/List';
-import { dropActionType } from '../../../util/DragManager';
+import { SchemaRowBox } from './SchemaRowBox';
 
 export enum ColumnType {
     Number,
@@ -23,11 +22,10 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data',
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
-    isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined);
+    private _lastSelectedRow: number | undefined;
 
-    @computed get layoutDoc() {
-        return Doc.Layout(this.props.Document);
-    }
+    @observable _rowMenuWidth: number = 60;
+    @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
 
     @computed get columnKeys() {
         return Cast(this.props.Document.columnKeys, listSpec('string'), defaultColumnKeys);
@@ -37,7 +35,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         return Cast(
             this.props.Document.columnWidths,
             listSpec('number'),
-            this.columnKeys.map(() => (this.props.PanelWidth() - 100) / this.columnKeys.length)
+            this.columnKeys.map(() => (this.props.PanelWidth() - this._rowMenuWidth) / this.columnKeys.length)
         );
     }
 
@@ -49,6 +47,30 @@ export class CollectionSchemaView extends CollectionSubView() {
         return true;
     };
 
+    @action
+    selectRow = (doc: Doc, ctrl?: boolean, shift?: boolean) => {
+        if (shift && this._lastSelectedRow !== undefined) {
+            const currSelectedRow = this.childDocs.indexOf(doc);
+            const startRow = Math.min(this._lastSelectedRow, currSelectedRow);
+            const endRow = Math.max(this._lastSelectedRow, currSelectedRow);
+            for (let i = startRow; i <= endRow; i++) {
+                const currDoc = this.childDocs[i];
+                if (!this._selectedDocs.has(currDoc)) this._selectedDocs.add(currDoc);
+            }
+        } else if (ctrl) {
+            if (!this._selectedDocs.has(doc)) {
+                this._selectedDocs.add(doc);
+                this._lastSelectedRow = this.childDocs.indexOf(doc);
+            } else {
+                this._selectedDocs.delete(doc);
+            }
+        } else {
+            this._selectedDocs.clear();
+            this._selectedDocs.add(doc);
+            this._lastSelectedRow = this.childDocs.indexOf(doc);
+        }
+    };
+
     render() {
         return (
             <div className="collectionSchemaView">
@@ -63,17 +85,13 @@ export class CollectionSchemaView extends CollectionSubView() {
                             <SchemaRowBox
                                 {...this.props}
                                 Document={doc}
-                                renderDepth={this.props.renderDepth + 1}
-                                isContentActive={this.isChildContentActive}
-                                rootSelected={this.rootSelected}
-                                dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
-                                docFilters={this.childDocFilters}
-                                docRangeFilters={this.childDocRangeFilters}
-                                searchFilterDocs={this.searchFilterDocs}
                                 ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                                 ContainingCollectionView={this.props.CollectionView}
                                 columnKeys={this.columnKeys}
                                 columnWidths={this.columnWidths}
+                                rowMenuWidth={this._rowMenuWidth}
+                                selectedRows={this._selectedDocs}
+                                selectRow={this.selectRow}
                             />
                         ))}
                     </div>
@@ -82,6 +100,3 @@ export class CollectionSchemaView extends CollectionSubView() {
         );
     }
 }
-function DocListCast(childDocs: Doc[]): any {
-    throw new Error('Function not implemented.');
-}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 50e2502dc..33f4e0bfc 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,25 +1,42 @@
 import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, ObservableMap, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
-import './CollectionSchemaView.scss';
-import { ViewBoxAnnotatableComponent, ViewBoxBaseComponent } from '../../DocComponent';
+import { Doc } from '../../../../fields/Doc';
+import { undoBatch } from '../../../util/UndoManager';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { Colors } from '../../global/globalEnums';
 import { FieldViewProps } from '../../nodes/FieldView';
+import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { undoBatch } from '../../../util/UndoManager';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     columnKeys: string[];
     columnWidths: number[];
+    rowMenuWidth: number;
+    selectedRows: ObservableSet<Doc>;
+    selectRow: (doc: Doc, cmd?: boolean, shift?: boolean) => void;
 }
 
 @observer
-export class SchemaRowBox extends ViewBoxAnnotatableComponent<SchemaRowBoxProps>() {
+export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+    isSelected = () => this.props.selectedRows.has(this.props.Document);
+
+    @action
+    onRowPointerDown = (e: React.PointerEvent) => {
+        e.stopPropagation();
+        this.props.selectRow(this.props.Document, e.ctrlKey || e.metaKey, e.shiftKey);
+    }
+
     render() {
         return (
-            <div className="schema-row" /*style={this.props.isSelected() ? { backgroundColor: 'grey' } : {}}*/>
-                <div className="row-menu">
-                    <div className="row-button" onPointerDown={undoBatch(() => this.props.removeDocument?.(this.props.Document))}>
-                        <FontAwesomeIcon icon={'times'} />
+            <div className="schema-row" style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE } : {}} onPointerDown={this.onRowPointerDown}>
+                <div className="row-menu" style={{width: this.props.rowMenuWidth}}>
+                    <div className="row-button" onPointerDown={undoBatch((e) => {e.stopPropagation(); this.props.removeDocument?.(this.props.Document)})}>
+                        <FontAwesomeIcon icon="times" />
+                    </div>
+                    <div className="row-button" onPointerDown={(e) => {e.stopPropagation(); this.props.addDocTab(this.props.Document, 'add:right')}}>
+                        <FontAwesomeIcon icon="external-link-alt" />
                     </div>
                 </div>
                 <div className="row-cells">
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 8437736ae..7bbd7c055 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -18,6 +18,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import './AudioBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
+import { SelectionManager } from '../../util/SelectionManager';
 
 /**
  * AudioBox
-- 
cgit v1.2.3-70-g09d2


From 5c4b22b50e4693419daac777669b258b155f6ea9 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 11 Aug 2022 13:08:56 -0400
Subject: bad dragging code in progress

---
 .../collectionSchema/CollectionSchemaView.tsx      | 75 ++++++++++++++++++++--
 .../collections/collectionSchema/SchemaRowBox.tsx  | 53 ++++++++++++++-
 2 files changed, 119 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 20d809232..9ca1644e3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -9,6 +9,10 @@ import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
+import { DragManager } from '../../../util/DragManager';
+import { PresBox } from '../../nodes/trails/PresBox';
+import { UndoManager } from '../../../util/UndoManager';
+import { returnFalse } from '../../../../Utils';
 
 export enum ColumnType {
     Number,
@@ -25,7 +29,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _lastSelectedRow: number | undefined;
 
     @observable _rowMenuWidth: number = 60;
-    @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
+    @observable _selectedDocs: ObservableSet = new ObservableSet<SchemaRowBox>();
+    @observable _isDragging: boolean = false;
 
     @computed get columnKeys() {
         return Cast(this.props.Document.columnKeys, listSpec('string'), defaultColumnKeys);
@@ -48,15 +53,18 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    selectRow = (doc: Doc, ctrl?: boolean, shift?: boolean) => {
+    selectRow = (e: React.PointerEvent, doc: Doc) => {
+        const ctrl = e.ctrlKey || e.metaKey;
+        const shift = e.shiftKey;
         if (shift && this._lastSelectedRow !== undefined) {
-            const currSelectedRow = this.childDocs.indexOf(doc);
-            const startRow = Math.min(this._lastSelectedRow, currSelectedRow);
-            const endRow = Math.max(this._lastSelectedRow, currSelectedRow);
+            const currRowIndex = this.childDocs.indexOf(doc);
+            const startRow = Math.min(this._lastSelectedRow, currRowIndex);
+            const endRow = Math.max(this._lastSelectedRow, currRowIndex);
             for (let i = startRow; i <= endRow; i++) {
-                const currDoc = this.childDocs[i];
+                const currDoc: Doc = this.childDocs[i];
                 if (!this._selectedDocs.has(currDoc)) this._selectedDocs.add(currDoc);
             }
+            this._lastSelectedRow = endRow;
         } else if (ctrl) {
             if (!this._selectedDocs.has(doc)) {
                 this._selectedDocs.add(doc);
@@ -71,6 +79,58 @@ export class CollectionSchemaView extends CollectionSubView() {
         }
     };
 
+    @action
+    sortedSelectedDocs = (): Doc[] => {
+        return this.childDocs.filter(doc => this._selectedDocs.has(doc));
+    };
+
+    @action.bound
+    moveDocument = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => {
+        console.log('hello');
+        console.log(targetCollection?.title);
+        if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
+            console.log('hi');
+            return true;
+        }
+        const first = document instanceof Doc ? document : document[0];
+        if (!first?._stayInCollection && addDocument !== returnFalse) {
+            UndoManager.RunInTempBatch(() => this.props.removeDocument?.(document) && addDocument(document));
+            return true;
+        }
+        return false;
+    };
+
+    @action
+    startDrag = (e: React.PointerEvent, doc: Doc) => {
+        if (this._selectedDocs.size === 0) this._selectedDocs.add(doc);
+        this._isDragging = true;
+        const dragData = new DragManager.DocumentDragData(this.sortedSelectedDocs(), 'move');
+        dragData.moveDocument = this.moveDocument;
+        const dragItem: HTMLElement[] = [];
+        const dragDiv = document.createElement('div');
+        dragDiv.className = 'presItem-multiDrag';
+        dragDiv.innerText = 'Move ' + this._selectedDocs.size + '  rows';
+        dragDiv.style.position = 'absolute';
+        dragDiv.style.top = e.clientY + 'px';
+        dragDiv.style.left = e.clientX - 50 + 'px';
+        dragItem.push(dragDiv);
+
+        DragManager.StartDocumentDrag(
+            dragItem.map(ele => ele),
+            dragData,
+            e.clientX,
+            e.clientY,
+            undefined
+        );
+        this._isDragging = false;
+        return true;
+    };
+
+    @action
+    endDrag = (e: React.PointerEvent) => {
+        // this._isDragging = false;
+    };
+
     render() {
         return (
             <div className="collectionSchemaView">
@@ -92,6 +152,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 rowMenuWidth={this._rowMenuWidth}
                                 selectedRows={this._selectedDocs}
                                 selectRow={this.selectRow}
+                                startDrag={this.startDrag}
+                                endDrag={this.endDrag}
+                                dragging={this._isDragging}
                             />
                         ))}
                     </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 33f4e0bfc..0065a0938 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -9,28 +9,75 @@ import { Colors } from '../../global/globalEnums';
 import { FieldViewProps } from '../../nodes/FieldView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
+import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
+import { DragManager } from '../../../util/DragManager';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     columnKeys: string[];
     columnWidths: number[];
     rowMenuWidth: number;
     selectedRows: ObservableSet<Doc>;
-    selectRow: (doc: Doc, cmd?: boolean, shift?: boolean) => void;
+    selectRow: (e: any, doc: Doc) => void;
+    startDrag: (e: any, doc: Doc) => boolean;
+    endDrag: (e: any) => void
+    dragging: boolean;
 }
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+    private _ref: HTMLDivElement | null = null;
+
     isSelected = () => this.props.selectedRows.has(this.props.Document);
+    bounds = () => this._ref?.getBoundingClientRect();
 
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
-        this.props.selectRow(this.props.Document, e.ctrlKey || e.metaKey, e.shiftKey);
+
+        setupMoveUpEvents(
+            this, e,
+            (e) => this.props.startDrag(e, this.props.Document),
+            emptyFunction,
+            (e) => this.props.selectRow(e, this.props.Document)
+        )
     }
 
+    onPointerEnter = (e: any) => {
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.addEventListener('pointermove', this.onPointerMove);
+    };
+
+    onPointerMove = (e: any) => {
+        let dragIsRow: boolean = true;
+        DragManager.docsBeingDragged.forEach(doc => {
+            dragIsRow = this.props.selectedRows.has(doc);
+        })
+        if (this._ref && dragIsRow) {
+            const rect = this._ref.getBoundingClientRect();
+            const y = e.clientY - rect.top; //y position within the element.
+            const height = this._ref.clientHeight;
+            const halfLine = height / 2;
+            if (y <= halfLine) {
+                this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
+                this._ref.style.borderBottom = '0px';
+            } else if (y > halfLine) {
+                this._ref.style.borderTop = '0px';
+                this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
+            }
+        }
+    };
+
+    onPointerLeave = (e: any) => {
+        if (this._ref) {
+            this._ref.style.borderTop = '0px';
+            this._ref.style.borderBottom = '0px';
+        }
+        document.removeEventListener('pointermove', this.onPointerMove);
+    };
+
     render() {
         return (
-            <div className="schema-row" style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE } : {}} onPointerDown={this.onRowPointerDown}>
+            <div className="schema-row" style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1} : {}} onPointerDown={this.onRowPointerDown} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} ref={(row: HTMLDivElement | null) => (this._ref = row)}>
                 <div className="row-menu" style={{width: this.props.rowMenuWidth}}>
                     <div className="row-button" onPointerDown={undoBatch((e) => {e.stopPropagation(); this.props.removeDocument?.(this.props.Document)})}>
                         <FontAwesomeIcon icon="times" />
-- 
cgit v1.2.3-70-g09d2


From ee77b8ff032d4b06c2f0b6ac8153a32e54ab8355 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 11 Aug 2022 14:28:09 -0400
Subject: drag and drop rows works, needs cleaning up

---
 .../collectionSchema/CollectionSchemaView.scss     |  1 +
 .../collectionSchema/CollectionSchemaView.tsx      | 78 ++++++++++++----------
 .../collections/collectionSchema/SchemaRowBox.tsx  | 14 +++-
 3 files changed, 54 insertions(+), 39 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 0f4053127..5ead99c02 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -2,6 +2,7 @@
 
 .collectionSchemaView {
     cursor: default;
+    height: 100%;
 
     .schema-table {
         background-color: $white;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 9ca1644e3..dc0cd8eac 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,18 +1,15 @@
 import React = require('react');
-import { action, computed, observable, ObservableMap, ObservableSet } from 'mobx';
+import { action, computed, observable, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { Cast } from '../../../../fields/Types';
+import { DragManager } from '../../../util/DragManager';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
-import { DragManager } from '../../../util/DragManager';
-import { PresBox } from '../../nodes/trails/PresBox';
-import { UndoManager } from '../../../util/UndoManager';
-import { returnFalse } from '../../../../Utils';
 
 export enum ColumnType {
     Number,
@@ -26,9 +23,12 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data',
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
+    private _ref: HTMLDivElement | null = null;
     private _lastSelectedRow: number | undefined;
+    private _selectedDocSortedArray: Doc[] = [];
+    private _closestDropIndex: number = 0;
 
-    @observable _rowMenuWidth: number = 60;
+    @observable _rowMenuWidth: number = 100;
     @observable _selectedDocs: ObservableSet = new ObservableSet<SchemaRowBox>();
     @observable _isDragging: boolean = false;
 
@@ -53,13 +53,12 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    selectRow = (e: React.PointerEvent, doc: Doc) => {
+    selectRow = (e: React.PointerEvent, doc: Doc, index: number) => {
         const ctrl = e.ctrlKey || e.metaKey;
         const shift = e.shiftKey;
         if (shift && this._lastSelectedRow !== undefined) {
-            const currRowIndex = this.childDocs.indexOf(doc);
-            const startRow = Math.min(this._lastSelectedRow, currRowIndex);
-            const endRow = Math.max(this._lastSelectedRow, currRowIndex);
+            const startRow = Math.min(this._lastSelectedRow, index);
+            const endRow = Math.max(this._lastSelectedRow, index);
             for (let i = startRow; i <= endRow; i++) {
                 const currDoc: Doc = this.childDocs[i];
                 if (!this._selectedDocs.has(currDoc)) this._selectedDocs.add(currDoc);
@@ -68,14 +67,14 @@ export class CollectionSchemaView extends CollectionSubView() {
         } else if (ctrl) {
             if (!this._selectedDocs.has(doc)) {
                 this._selectedDocs.add(doc);
-                this._lastSelectedRow = this.childDocs.indexOf(doc);
+                this._lastSelectedRow = index;
             } else {
                 this._selectedDocs.delete(doc);
             }
         } else {
             this._selectedDocs.clear();
             this._selectedDocs.add(doc);
-            this._lastSelectedRow = this.childDocs.indexOf(doc);
+            this._lastSelectedRow = index;
         }
     };
 
@@ -84,17 +83,19 @@ export class CollectionSchemaView extends CollectionSubView() {
         return this.childDocs.filter(doc => this._selectedDocs.has(doc));
     };
 
-    @action.bound
-    moveDocument = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => {
-        console.log('hello');
-        console.log(targetCollection?.title);
-        if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
-            console.log('hi');
-            return true;
-        }
-        const first = document instanceof Doc ? document : document[0];
-        if (!first?._stayInCollection && addDocument !== returnFalse) {
-            UndoManager.RunInTempBatch(() => this.props.removeDocument?.(document) && addDocument(document));
+    setDropIndex = (index: number) => {
+        this._closestDropIndex = index;
+    };
+
+    @action
+    onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+        if (super.onInternalDrop(e, de)) {
+            this._isDragging = false;
+            const pushedDocs: Doc[] = this.childDocs.filter((doc: Doc, index: number) => index >= this._closestDropIndex && !this._selectedDocs.has(doc));
+            this.props.removeDocument?.(pushedDocs);
+            this.props.removeDocument?.(this._selectedDocSortedArray);
+            this.addDocument(this._selectedDocSortedArray);
+            this.addDocument(pushedDocs);
             return true;
         }
         return false;
@@ -102,14 +103,18 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     startDrag = (e: React.PointerEvent, doc: Doc) => {
-        if (this._selectedDocs.size === 0) this._selectedDocs.add(doc);
+        if (!this._selectedDocs.has(doc)) {
+            this._selectedDocs.clear();
+            this._selectedDocs.add(doc);
+        }
         this._isDragging = true;
-        const dragData = new DragManager.DocumentDragData(this.sortedSelectedDocs(), 'move');
-        dragData.moveDocument = this.moveDocument;
+        this._selectedDocSortedArray = this.sortedSelectedDocs();
+        const dragData = new DragManager.DocumentDragData(this._selectedDocSortedArray, 'move');
+        dragData.moveDocument = this.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         const dragDiv = document.createElement('div');
         dragDiv.className = 'presItem-multiDrag';
-        dragDiv.innerText = 'Move ' + this._selectedDocs.size + '  rows';
+        dragDiv.innerText = 'Move ' + this._selectedDocs.size + '  row' + (this._selectedDocs.size > 1 ? 's' : '');
         dragDiv.style.position = 'absolute';
         dragDiv.style.top = e.clientY + 'px';
         dragDiv.style.left = e.clientX - 50 + 'px';
@@ -122,18 +127,18 @@ export class CollectionSchemaView extends CollectionSubView() {
             e.clientY,
             undefined
         );
-        this._isDragging = false;
         return true;
     };
 
-    @action
-    endDrag = (e: React.PointerEvent) => {
-        // this._isDragging = false;
-    };
-
     render() {
         return (
-            <div className="collectionSchemaView">
+            <div
+                className="collectionSchemaView"
+                ref={(ele: HTMLDivElement | null) => {
+                    this._ref = ele;
+                    this.createDashEventsTarget(ele);
+                }}
+                onPointerDown={() => this._selectedDocs.clear()}>
                 <div className="schema-table">
                     <div className="schema-header-row">
                         {this.columnKeys.map((key, index) => (
@@ -141,20 +146,21 @@ export class CollectionSchemaView extends CollectionSubView() {
                         ))}
                     </div>
                     <div className="schema-table-content">
-                        {this.childDocs.map((doc: Doc) => (
+                        {this.childDocs.map((doc: Doc, index: number) => (
                             <SchemaRowBox
                                 {...this.props}
                                 Document={doc}
                                 ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                                 ContainingCollectionView={this.props.CollectionView}
+                                rowIndex={index}
                                 columnKeys={this.columnKeys}
                                 columnWidths={this.columnWidths}
                                 rowMenuWidth={this._rowMenuWidth}
                                 selectedRows={this._selectedDocs}
                                 selectRow={this.selectRow}
                                 startDrag={this.startDrag}
-                                endDrag={this.endDrag}
                                 dragging={this._isDragging}
+                                dropIndex={this.setDropIndex}
                             />
                         ))}
                     </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 0065a0938..8b658d834 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -13,14 +13,15 @@ import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
+    rowIndex: number;
     columnKeys: string[];
     columnWidths: number[];
     rowMenuWidth: number;
     selectedRows: ObservableSet<Doc>;
-    selectRow: (e: any, doc: Doc) => void;
+    selectRow: (e: any, doc: Doc, index: number) => void;
     startDrag: (e: any, doc: Doc) => boolean;
-    endDrag: (e: any) => void
     dragging: boolean;
+    dropIndex: (index: number) => void;
 }
 
 @observer
@@ -38,16 +39,18 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
             this, e,
             (e) => this.props.startDrag(e, this.props.Document),
             emptyFunction,
-            (e) => this.props.selectRow(e, this.props.Document)
+            (e) => this.props.selectRow(e, this.props.Document, this.props.rowIndex)
         )
     }
 
     onPointerEnter = (e: any) => {
+        if (!this.props.dragging) return;
         document.removeEventListener('pointermove', this.onPointerMove);
         document.addEventListener('pointermove', this.onPointerMove);
     };
 
     onPointerMove = (e: any) => {
+        if (!this.props.dragging) return;
         let dragIsRow: boolean = true;
         DragManager.docsBeingDragged.forEach(doc => {
             dragIsRow = this.props.selectedRows.has(doc);
@@ -60,9 +63,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
             if (y <= halfLine) {
                 this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
                 this._ref.style.borderBottom = '0px';
+                this.props.dropIndex(this.props.rowIndex);
             } else if (y > halfLine) {
                 this._ref.style.borderTop = '0px';
                 this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
+                this.props.dropIndex(this.props.rowIndex + 1);
             }
         }
     };
@@ -85,6 +90,9 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                     <div className="row-button" onPointerDown={(e) => {e.stopPropagation(); this.props.addDocTab(this.props.Document, 'add:right')}}>
                         <FontAwesomeIcon icon="external-link-alt" />
                     </div>
+                    <div className="row-button">
+                        {this.props.rowIndex}
+                    </div>
                 </div>
                 <div className="row-cells">
                     {this.props.columnKeys.map((key, index) => (
-- 
cgit v1.2.3-70-g09d2


From d0d10426510e1bc8397eea927a65714b5b48f966 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 15 Aug 2022 22:36:56 -0400
Subject: add new doc as schema row

---
 .../collectionSchema/CollectionSchemaView.scss     |  11 ++-
 .../collectionSchema/CollectionSchemaView.tsx      | 107 ++++++++++++++++++++-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   7 +-
 3 files changed, 111 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 5ead99c02..4f644717b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -31,7 +31,7 @@
         }
 
         .schema-row {
-            justify-content: space-evenly;
+            justify-content: flex-end;
 
             .row-menu {
                 display: flex;
@@ -39,13 +39,16 @@
                 justify-content: center;
 
                 .row-button {
-                    width: 20px;
-                    height: 20px;
+                    width: 25px;
+                    height: 25px;
                     border-radius: 100%;
                     background-color: $dark-gray;
                     color: white;
-                    padding: 5px;
+                    margin: 3px;
                     cursor: pointer;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
                 }
             }
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index dc0cd8eac..02bc55e7a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,15 +1,26 @@
 import React = require('react');
 import { action, computed, observable, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc } from '../../../../fields/Doc';
+import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
-import { Cast } from '../../../../fields/Types';
+import { Cast, StrCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { undoBatch } from '../../../util/UndoManager';
+import { EditableView } from '../../EditableView';
+import { returnEmptyString } from '../../../../Utils';
+import { Docs, DocUtils } from '../../../documents/Documents';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { ContextMenu } from '../../ContextMenu';
+import { ContextMenuProps } from '../../ContextMenuItem';
+import { Id } from '../../../../fields/FieldSymbols';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { ImageField } from '../../../../fields/URLField';
 
 export enum ColumnType {
     Number,
@@ -33,18 +44,19 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _isDragging: boolean = false;
 
     @computed get columnKeys() {
-        return Cast(this.props.Document.columnKeys, listSpec('string'), defaultColumnKeys);
+        return Cast(this.layoutDoc.columnKeys, listSpec('string'), defaultColumnKeys);
     }
 
     @computed get columnWidths() {
         return Cast(
-            this.props.Document.columnWidths,
+            this.layoutDoc.columnWidths,
             listSpec('number'),
             this.columnKeys.map(() => (this.props.PanelWidth() - this._rowMenuWidth) / this.columnKeys.length)
         );
     }
 
     @action
+    @undoBatch
     changeColumnKey = (index: number, newKey: string) => {
         let currKeys = this.columnKeys;
         currKeys[index] = newKey;
@@ -76,6 +88,13 @@ export class CollectionSchemaView extends CollectionSubView() {
             this._selectedDocs.add(doc);
             this._lastSelectedRow = index;
         }
+        
+        if (this._lastSelectedRow && this._selectedDocs.size > 0) {
+            SelectionManager.SelectSchemaViewDoc(this.childDocs[this._lastSelectedRow]);
+        }
+        else {
+            SelectionManager.SelectSchemaViewDoc(undefined);
+        }
     };
 
     @action
@@ -106,6 +125,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         if (!this._selectedDocs.has(doc)) {
             this._selectedDocs.clear();
             this._selectedDocs.add(doc);
+            this._lastSelectedRow = this.childDocs.indexOf(doc);
+            SelectionManager.SelectSchemaViewDoc(doc);
         }
         this._isDragging = true;
         this._selectedDocSortedArray = this.sortedSelectedDocs();
@@ -130,6 +151,83 @@ export class CollectionSchemaView extends CollectionSubView() {
         return true;
     };
 
+    @action
+    addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
+        if (!value && !forceEmptyNote) return false;
+        const newDoc = Docs.Create.TextDocument(value, {title: value});
+        FormattedTextBox.SelectOnLoad = newDoc[Id];
+        FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
+        return this.props.addDocument?.(newDoc) || false;
+    };
+
+    menuCallback = (x: number, y: number) => {
+        ContextMenu.Instance.clearItems();
+        const layoutItems: ContextMenuProps[] = [];
+        const docItems: ContextMenuProps[] = [];
+        const dataDoc = this.props.DataDoc || this.props.Document;
+
+        DocUtils.addDocumentCreatorMenuItems(
+            doc => {
+                FormattedTextBox.SelectOnLoad = StrCast(doc[Id]);
+                return this.addDocument(doc);
+            },
+            this.addDocument,
+            x,
+            y,
+            true
+        );
+
+        Array.from(Object.keys(Doc.GetProto(dataDoc)))
+            .filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof dataDoc[fieldKey] === 'string')
+            .map(fieldKey =>
+                docItems.push({
+                    description: ':' + fieldKey,
+                    event: () => {
+                        const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document));
+                        if (created) {
+                            if (this.props.Document.isTemplateDoc) {
+                                Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+                            }
+                            return this.props.addDocument?.(created);
+                        }
+                    },
+                    icon: 'compress-arrows-alt',
+                })
+            );
+        Array.from(Object.keys(Doc.GetProto(dataDoc)))
+            .filter(fieldKey => DocListCast(dataDoc[fieldKey]).length)
+            .map(fieldKey =>
+                docItems.push({
+                    description: ':' + fieldKey,
+                    event: () => {
+                        const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
+                        if (created) {
+                            const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document;
+                            if (container.isTemplateDoc) {
+                                Doc.MakeMetadataFieldTemplate(created, container);
+                                return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
+                            }
+                            return this.props.addDocument?.(created) || false;
+                        }
+                    },
+                    icon: 'compress-arrows-alt',
+                })
+            );
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' });
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
+        ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
+            Doc.GetProto(this.props.Document)[name] = '';
+            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true });
+            if (created) {
+                if (this.props.Document.isTemplateDoc) {
+                    Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+                }
+                this.props.addDocument?.(created);
+            }
+        });
+        ContextMenu.Instance.displayMenu(x, y, undefined, true);
+    };
+
     render() {
         return (
             <div
@@ -165,6 +263,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                         ))}
                     </div>
                 </div>
+                <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 8b658d834..ea2ebed7a 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -90,14 +90,9 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                     <div className="row-button" onPointerDown={(e) => {e.stopPropagation(); this.props.addDocTab(this.props.Document, 'add:right')}}>
                         <FontAwesomeIcon icon="external-link-alt" />
                     </div>
-                    <div className="row-button">
-                        {this.props.rowIndex}
-                    </div>
                 </div>
                 <div className="row-cells">
-                    {this.props.columnKeys.map((key, index) => (
-                        <SchemaTableCell Document={this.props.Document} key={key} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
-                    ))}
+                    {this.props.columnKeys.map((key, index) => (<SchemaTableCell Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />))}
                 </div>
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From a20a40b86bf5df9200218cb713f48e593c41b0ae Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 16 Aug 2022 17:28:33 -0400
Subject: got formatter to work

---
 package-lock.json                                  | 1349 +++++++-------------
 .../collectionSchema/CollectionSchemaView.tsx      |    7 +-
 .../collectionSchema/SchemaColumnHeader.tsx        |    5 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   41 +-
 .../collectionSchema/SchemaTableCell.tsx           |    1 -
 5 files changed, 500 insertions(+), 903 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index cafeebd3e..da921592a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5050,16 +5050,6 @@
             "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6112,8 +6102,8 @@
             "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
         },
         "equation-editor-react": {
-            "version": "github:bobzel/equation-editor-react#75915e852b4b36a6a4cd3e1cbc80598da6b65227",
-            "from": "github:bobzel/equation-editor-react#useLocally",
+            "version": "git+ssh://git@github.com/bobzel/equation-editor-react.git#75915e852b4b36a6a4cd3e1cbc80598da6b65227",
+            "from": "equation-editor-react@github:bobzel/equation-editor-react#useLocally",
             "requires": {
                 "jquery": "^3.4.1",
                 "mathquill": "^0.10.1-a"
@@ -6210,9 +6200,9 @@
             }
         },
         "es5-ext": {
-            "version": "0.10.61",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
-            "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
             "dev": true,
             "requires": {
                 "es6-iterator": "^2.0.3",
@@ -6229,6 +6219,18 @@
                 "d": "1",
                 "es5-ext": "^0.10.35",
                 "es6-symbol": "^3.1.1"
+            },
+            "dependencies": {
+                "d": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+                    "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+                    "dev": true,
+                    "requires": {
+                        "es5-ext": "^0.10.50",
+                        "type": "^1.0.1"
+                    }
+                }
             }
         },
         "es6-promise": {
@@ -6244,6 +6246,18 @@
             "requires": {
                 "d": "^1.0.1",
                 "ext": "^1.1.2"
+            },
+            "dependencies": {
+                "d": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+                    "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+                    "dev": true,
+                    "requires": {
+                        "es5-ext": "^0.10.50",
+                        "type": "^1.0.1"
+                    }
+                }
             }
         },
         "escalade": {
@@ -9500,14 +9514,14 @@
             "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
             "integrity": "sha512-N505B5FSy2Xf5l/Haef+99TwfJqTu40hnU560+rC0Cm6cxtwVz2yRFh9WpOk1YEjfv3dI0PgVYAH0hmXQmjDcw==",
             "requires": {
-                "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                "image-size": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
                 "readable-stream": "^1.0.33",
                 "tryit": "^1.0.1"
             },
             "dependencies": {
                 "image-size": {
-                    "version": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
-                    "from": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
+                    "version": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                    "from": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
                 },
                 "isarray": {
                     "version": "0.0.1",
@@ -12505,70 +12519,59 @@
                 },
                 "abbrev": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-                    "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+                    "bundled": true
                 },
                 "agent-base": {
                     "version": "4.3.0",
-                    "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
-                    "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+                    "bundled": true,
                     "requires": {
                         "es6-promisify": "^5.0.0"
                     }
                 },
                 "agentkeepalive": {
                     "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
-                    "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
+                    "bundled": true,
                     "requires": {
                         "humanize-ms": "^1.2.1"
                     }
                 },
                 "ansi-align": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
-                    "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^2.0.0"
                     }
                 },
                 "ansi-regex": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+                    "bundled": true
                 },
                 "ansi-styles": {
                     "version": "3.2.1",
-                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-                    "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+                    "bundled": true,
                     "requires": {
                         "color-convert": "^1.9.0"
                     }
                 },
                 "ansicolors": {
                     "version": "0.3.2",
-                    "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
-                    "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="
+                    "bundled": true
                 },
                 "ansistyles": {
                     "version": "0.1.3",
-                    "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz",
-                    "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk="
+                    "bundled": true
                 },
                 "aproba": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-                    "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+                    "bundled": true
                 },
                 "archy": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
-                    "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
+                    "bundled": true
                 },
                 "are-we-there-yet": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
-                    "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+                    "bundled": true,
                     "requires": {
                         "delegates": "^1.0.0",
                         "readable-stream": "^2.0.6"
@@ -12576,8 +12579,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -12590,8 +12592,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -12600,46 +12601,38 @@
                 },
                 "asap": {
                     "version": "2.0.6",
-                    "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
-                    "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+                    "bundled": true
                 },
                 "asn1": {
                     "version": "0.2.4",
-                    "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-                    "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+                    "bundled": true,
                     "requires": {
                         "safer-buffer": "~2.1.0"
                     }
                 },
                 "assert-plus": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-                    "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+                    "bundled": true
                 },
                 "asynckit": {
                     "version": "0.4.0",
-                    "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-                    "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+                    "bundled": true
                 },
                 "aws-sign2": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-                    "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+                    "bundled": true
                 },
                 "aws4": {
                     "version": "1.8.0",
-                    "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-                    "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
+                    "bundled": true
                 },
                 "balanced-match": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-                    "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+                    "bundled": true
                 },
                 "bcrypt-pbkdf": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-                    "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+                    "bundled": true,
                     "optional": true,
                     "requires": {
                         "tweetnacl": "^0.14.3"
@@ -12647,8 +12640,7 @@
                 },
                 "bin-links": {
                     "version": "1.1.8",
-                    "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-1.1.8.tgz",
-                    "integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==",
+                    "bundled": true,
                     "requires": {
                         "bluebird": "^3.5.3",
                         "cmd-shim": "^3.0.0",
@@ -12660,13 +12652,11 @@
                 },
                 "bluebird": {
                     "version": "3.5.5",
-                    "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
-                    "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w=="
+                    "bundled": true
                 },
                 "boxen": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
-                    "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
+                    "bundled": true,
                     "requires": {
                         "ansi-align": "^2.0.0",
                         "camelcase": "^4.0.0",
@@ -12679,8 +12669,7 @@
                 },
                 "brace-expansion": {
                     "version": "1.1.11",
-                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-                    "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+                    "bundled": true,
                     "requires": {
                         "balanced-match": "^1.0.0",
                         "concat-map": "0.0.1"
@@ -12688,28 +12677,23 @@
                 },
                 "buffer-from": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
-                    "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA=="
+                    "bundled": true
                 },
                 "builtins": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
-                    "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og="
+                    "bundled": true
                 },
                 "byline": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz",
-                    "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE="
+                    "bundled": true
                 },
                 "byte-size": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz",
-                    "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw=="
+                    "bundled": true
                 },
                 "cacache": {
                     "version": "12.0.3",
-                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
-                    "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
+                    "bundled": true,
                     "requires": {
                         "bluebird": "^3.5.5",
                         "chownr": "^1.1.1",
@@ -12730,28 +12714,23 @@
                 },
                 "call-limit": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/call-limit/-/call-limit-1.1.1.tgz",
-                    "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ=="
+                    "bundled": true
                 },
                 "camelcase": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+                    "bundled": true
                 },
                 "capture-stack-trace": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
-                    "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
+                    "bundled": true
                 },
                 "caseless": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-                    "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+                    "bundled": true
                 },
                 "chalk": {
                     "version": "2.4.1",
-                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-                    "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+                    "bundled": true,
                     "requires": {
                         "ansi-styles": "^3.2.1",
                         "escape-string-regexp": "^1.0.5",
@@ -12760,31 +12739,26 @@
                 },
                 "chownr": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+                    "bundled": true
                 },
                 "ci-info": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
-                    "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+                    "bundled": true
                 },
                 "cidr-regex": {
                     "version": "2.0.10",
-                    "resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-2.0.10.tgz",
-                    "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==",
+                    "bundled": true,
                     "requires": {
                         "ip-regex": "^2.1.0"
                     }
                 },
                 "cli-boxes": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
-                    "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
+                    "bundled": true
                 },
                 "cli-columns": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz",
-                    "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^2.0.0",
                         "strip-ansi": "^3.0.1"
@@ -12792,8 +12766,7 @@
                 },
                 "cli-table3": {
                     "version": "0.5.1",
-                    "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
-                    "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
+                    "bundled": true,
                     "requires": {
                         "colors": "^1.1.2",
                         "object-assign": "^4.1.0",
@@ -12802,8 +12775,7 @@
                 },
                 "cliui": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-                    "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^3.1.0",
                         "strip-ansi": "^5.2.0",
@@ -12812,18 +12784,15 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "4.1.1",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                            "bundled": true
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "3.1.0",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-                            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                            "bundled": true,
                             "requires": {
                                 "emoji-regex": "^7.0.1",
                                 "is-fullwidth-code-point": "^2.0.0",
@@ -12832,8 +12801,7 @@
                         },
                         "strip-ansi": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-                            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^4.1.0"
                             }
@@ -12842,13 +12810,11 @@
                 },
                 "clone": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
-                    "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
+                    "bundled": true
                 },
                 "cmd-shim": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-3.0.3.tgz",
-                    "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "mkdirp": "~0.5.0"
@@ -12856,32 +12822,27 @@
                 },
                 "code-point-at": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-                    "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+                    "bundled": true
                 },
                 "color-convert": {
                     "version": "1.9.1",
-                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
-                    "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+                    "bundled": true,
                     "requires": {
                         "color-name": "^1.1.1"
                     }
                 },
                 "color-name": {
                     "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-                    "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+                    "bundled": true
                 },
                 "colors": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
-                    "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
+                    "bundled": true,
                     "optional": true
                 },
                 "columnify": {
                     "version": "1.5.4",
-                    "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz",
-                    "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=",
+                    "bundled": true,
                     "requires": {
                         "strip-ansi": "^3.0.0",
                         "wcwidth": "^1.0.0"
@@ -12889,21 +12850,18 @@
                 },
                 "combined-stream": {
                     "version": "1.0.6",
-                    "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
-                    "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+                    "bundled": true,
                     "requires": {
                         "delayed-stream": "~1.0.0"
                     }
                 },
                 "concat-map": {
                     "version": "0.0.1",
-                    "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-                    "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+                    "bundled": true
                 },
                 "concat-stream": {
                     "version": "1.6.2",
-                    "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-                    "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+                    "bundled": true,
                     "requires": {
                         "buffer-from": "^1.0.0",
                         "inherits": "^2.0.3",
@@ -12913,8 +12871,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -12927,8 +12884,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -12937,8 +12893,7 @@
                 },
                 "config-chain": {
                     "version": "1.1.12",
-                    "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
-                    "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
+                    "bundled": true,
                     "requires": {
                         "ini": "^1.3.4",
                         "proto-list": "~1.2.1"
@@ -12946,8 +12901,7 @@
                 },
                 "configstore": {
                     "version": "3.1.5",
-                    "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz",
-                    "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==",
+                    "bundled": true,
                     "requires": {
                         "dot-prop": "^4.2.1",
                         "graceful-fs": "^4.1.2",
@@ -12959,13 +12913,11 @@
                 },
                 "console-control-strings": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-                    "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+                    "bundled": true
                 },
                 "copy-concurrently": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
-                    "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.1",
                         "fs-write-stream-atomic": "^1.0.8",
@@ -12977,33 +12929,28 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-                            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
+                            "bundled": true
                         }
                     }
                 },
                 "core-util-is": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-                    "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+                    "bundled": true
                 },
                 "create-error-class": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
-                    "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+                    "bundled": true,
                     "requires": {
                         "capture-stack-trace": "^1.0.0"
                     }
                 },
                 "cross-spawn": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-                    "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+                    "bundled": true,
                     "requires": {
                         "lru-cache": "^4.0.1",
                         "shebang-command": "^1.2.0",
@@ -13012,8 +12959,7 @@
                     "dependencies": {
                         "lru-cache": {
                             "version": "4.1.5",
-                            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-                            "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+                            "bundled": true,
                             "requires": {
                                 "pseudomap": "^1.0.2",
                                 "yallist": "^2.1.2"
@@ -13021,104 +12967,87 @@
                         },
                         "yallist": {
                             "version": "2.1.2",
-                            "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-                            "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+                            "bundled": true
                         }
                     }
                 },
                 "crypto-random-string": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
-                    "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
+                    "bundled": true
                 },
                 "cyclist": {
                     "version": "0.2.2",
-                    "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
-                    "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
+                    "bundled": true
                 },
                 "dashdash": {
                     "version": "1.14.1",
-                    "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-                    "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0"
                     }
                 },
                 "debug": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+                    "bundled": true,
                     "requires": {
                         "ms": "2.0.0"
                     },
                     "dependencies": {
                         "ms": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                            "bundled": true
                         }
                     }
                 },
                 "debuglog": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
-                    "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI="
+                    "bundled": true
                 },
                 "decamelize": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-                    "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+                    "bundled": true
                 },
                 "decode-uri-component": {
                     "version": "0.2.0",
-                    "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-                    "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+                    "bundled": true
                 },
                 "deep-extend": {
                     "version": "0.6.0",
-                    "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
-                    "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+                    "bundled": true
                 },
                 "defaults": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
-                    "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+                    "bundled": true,
                     "requires": {
                         "clone": "^1.0.2"
                     }
                 },
                 "define-properties": {
                     "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-                    "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+                    "bundled": true,
                     "requires": {
                         "object-keys": "^1.0.12"
                     }
                 },
                 "delayed-stream": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-                    "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+                    "bundled": true
                 },
                 "delegates": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-                    "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+                    "bundled": true
                 },
                 "detect-indent": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz",
-                    "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50="
+                    "bundled": true
                 },
                 "detect-newline": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
-                    "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I="
+                    "bundled": true
                 },
                 "dezalgo": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
-                    "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
+                    "bundled": true,
                     "requires": {
                         "asap": "^2.0.0",
                         "wrappy": "1"
@@ -13126,26 +13055,22 @@
                 },
                 "dot-prop": {
                     "version": "4.2.1",
-                    "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
-                    "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
+                    "bundled": true,
                     "requires": {
                         "is-obj": "^1.0.0"
                     }
                 },
                 "dotenv": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
-                    "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
+                    "bundled": true
                 },
                 "duplexer3": {
                     "version": "0.1.4",
-                    "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
-                    "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
+                    "bundled": true
                 },
                 "duplexify": {
                     "version": "3.6.0",
-                    "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
-                    "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
+                    "bundled": true,
                     "requires": {
                         "end-of-stream": "^1.0.0",
                         "inherits": "^2.0.1",
@@ -13155,8 +13080,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -13169,8 +13093,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -13179,8 +13102,7 @@
                 },
                 "ecc-jsbn": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-                    "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+                    "bundled": true,
                     "optional": true,
                     "requires": {
                         "jsbn": "~0.1.0",
@@ -13189,52 +13111,44 @@
                 },
                 "editor": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz",
-                    "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I="
+                    "bundled": true
                 },
                 "emoji-regex": {
                     "version": "7.0.3",
-                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-                    "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
+                    "bundled": true
                 },
                 "encoding": {
                     "version": "0.1.12",
-                    "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
-                    "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+                    "bundled": true,
                     "requires": {
                         "iconv-lite": "~0.4.13"
                     }
                 },
                 "end-of-stream": {
                     "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
-                    "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+                    "bundled": true,
                     "requires": {
                         "once": "^1.4.0"
                     }
                 },
                 "env-paths": {
                     "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz",
-                    "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA=="
+                    "bundled": true
                 },
                 "err-code": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
-                    "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA="
+                    "bundled": true
                 },
                 "errno": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
-                    "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+                    "bundled": true,
                     "requires": {
                         "prr": "~1.0.1"
                     }
                 },
                 "es-abstract": {
                     "version": "1.12.0",
-                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
-                    "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
+                    "bundled": true,
                     "requires": {
                         "es-to-primitive": "^1.1.1",
                         "function-bind": "^1.1.1",
@@ -13245,8 +13159,7 @@
                 },
                 "es-to-primitive": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
-                    "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+                    "bundled": true,
                     "requires": {
                         "is-callable": "^1.1.4",
                         "is-date-object": "^1.0.1",
@@ -13255,26 +13168,22 @@
                 },
                 "es6-promise": {
                     "version": "4.2.8",
-                    "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
-                    "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+                    "bundled": true
                 },
                 "es6-promisify": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
-                    "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+                    "bundled": true,
                     "requires": {
                         "es6-promise": "^4.0.3"
                     }
                 },
                 "escape-string-regexp": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-                    "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+                    "bundled": true
                 },
                 "execa": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
-                    "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+                    "bundled": true,
                     "requires": {
                         "cross-spawn": "^5.0.1",
                         "get-stream": "^3.0.0",
@@ -13287,40 +13196,33 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-                            "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+                            "bundled": true
                         }
                     }
                 },
                 "extend": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-                    "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+                    "bundled": true
                 },
                 "extsprintf": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-                    "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+                    "bundled": true
                 },
                 "fast-json-stable-stringify": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-                    "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+                    "bundled": true
                 },
                 "figgy-pudding": {
                     "version": "3.5.1",
-                    "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
-                    "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w=="
+                    "bundled": true
                 },
                 "find-npm-prefix": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz",
-                    "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA=="
+                    "bundled": true
                 },
                 "flush-write-stream": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
-                    "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
+                    "bundled": true,
                     "requires": {
                         "inherits": "^2.0.1",
                         "readable-stream": "^2.0.4"
@@ -13328,8 +13230,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -13342,8 +13243,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -13352,13 +13252,11 @@
                 },
                 "forever-agent": {
                     "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-                    "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+                    "bundled": true
                 },
                 "form-data": {
                     "version": "2.3.2",
-                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
-                    "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+                    "bundled": true,
                     "requires": {
                         "asynckit": "^0.4.0",
                         "combined-stream": "1.0.6",
@@ -13367,8 +13265,7 @@
                 },
                 "from2": {
                     "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-                    "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+                    "bundled": true,
                     "requires": {
                         "inherits": "^2.0.1",
                         "readable-stream": "^2.0.0"
@@ -13376,8 +13273,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -13390,8 +13286,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -13400,16 +13295,14 @@
                 },
                 "fs-minipass": {
                     "version": "1.2.7",
-                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
-                    "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
+                    "bundled": true,
                     "requires": {
                         "minipass": "^2.6.0"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -13419,8 +13312,7 @@
                 },
                 "fs-vacuum": {
                     "version": "1.2.10",
-                    "resolved": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz",
-                    "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "path-is-inside": "^1.0.1",
@@ -13429,8 +13321,7 @@
                 },
                 "fs-write-stream-atomic": {
                     "version": "1.0.10",
-                    "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
-                    "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "iferr": "^0.1.5",
@@ -13440,13 +13331,11 @@
                     "dependencies": {
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-                            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
+                            "bundled": true
                         },
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -13459,8 +13348,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -13469,18 +13357,15 @@
                 },
                 "fs.realpath": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-                    "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+                    "bundled": true
                 },
                 "function-bind": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-                    "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+                    "bundled": true
                 },
                 "gauge": {
                     "version": "2.7.4",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-                    "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.0.3",
                         "console-control-strings": "^1.0.0",
@@ -13494,13 +13379,11 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                            "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                            "bundled": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -13511,13 +13394,11 @@
                 },
                 "genfun": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz",
-                    "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA=="
+                    "bundled": true
                 },
                 "gentle-fs": {
                     "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/gentle-fs/-/gentle-fs-2.3.1.tgz",
-                    "integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.2",
                         "chownr": "^1.1.2",
@@ -13534,41 +13415,35 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-                            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
+                            "bundled": true
                         }
                     }
                 },
                 "get-caller-file": {
                     "version": "2.0.5",
-                    "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-                    "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+                    "bundled": true
                 },
                 "get-stream": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
-                    "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+                    "bundled": true,
                     "requires": {
                         "pump": "^3.0.0"
                     }
                 },
                 "getpass": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-                    "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0"
                     }
                 },
                 "glob": {
                     "version": "7.1.6",
-                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-                    "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+                    "bundled": true,
                     "requires": {
                         "fs.realpath": "^1.0.0",
                         "inflight": "^1.0.4",
@@ -13580,16 +13455,14 @@
                 },
                 "global-dirs": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
-                    "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+                    "bundled": true,
                     "requires": {
                         "ini": "^1.3.4"
                     }
                 },
                 "got": {
                     "version": "6.7.1",
-                    "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
-                    "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+                    "bundled": true,
                     "requires": {
                         "create-error-class": "^3.0.0",
                         "duplexer3": "^0.1.4",
@@ -13606,25 +13479,21 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-                            "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+                            "bundled": true
                         }
                     }
                 },
                 "graceful-fs": {
                     "version": "4.2.4",
-                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
-                    "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
+                    "bundled": true
                 },
                 "har-schema": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-                    "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+                    "bundled": true
                 },
                 "har-validator": {
                     "version": "5.1.5",
-                    "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
-                    "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+                    "bundled": true,
                     "requires": {
                         "ajv": "^6.12.3",
                         "har-schema": "^2.0.0"
@@ -13632,8 +13501,7 @@
                     "dependencies": {
                         "ajv": {
                             "version": "6.12.6",
-                            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-                            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+                            "bundled": true,
                             "requires": {
                                 "fast-deep-equal": "^3.1.1",
                                 "fast-json-stable-stringify": "^2.0.0",
@@ -13643,53 +13511,44 @@
                         },
                         "fast-deep-equal": {
                             "version": "3.1.3",
-                            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-                            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                            "bundled": true
                         },
                         "json-schema-traverse": {
                             "version": "0.4.1",
-                            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-                            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+                            "bundled": true
                         }
                     }
                 },
                 "has": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-                    "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+                    "bundled": true,
                     "requires": {
                         "function-bind": "^1.1.1"
                     }
                 },
                 "has-flag": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-                    "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+                    "bundled": true
                 },
                 "has-symbols": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-                    "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
+                    "bundled": true
                 },
                 "has-unicode": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-                    "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+                    "bundled": true
                 },
                 "hosted-git-info": {
                     "version": "2.8.9",
-                    "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
-                    "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+                    "bundled": true
                 },
                 "http-cache-semantics": {
                     "version": "3.8.1",
-                    "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
-                    "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w=="
+                    "bundled": true
                 },
                 "http-proxy-agent": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
-                    "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
+                    "bundled": true,
                     "requires": {
                         "agent-base": "4",
                         "debug": "3.1.0"
@@ -13697,8 +13556,7 @@
                 },
                 "http-signature": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-                    "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0",
                         "jsprim": "^1.2.2",
@@ -13707,8 +13565,7 @@
                 },
                 "https-proxy-agent": {
                     "version": "2.2.4",
-                    "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
-                    "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+                    "bundled": true,
                     "requires": {
                         "agent-base": "^4.3.0",
                         "debug": "^3.1.0"
@@ -13716,52 +13573,44 @@
                 },
                 "humanize-ms": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
-                    "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+                    "bundled": true,
                     "requires": {
                         "ms": "^2.0.0"
                     }
                 },
                 "iconv-lite": {
                     "version": "0.4.23",
-                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-                    "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+                    "bundled": true,
                     "requires": {
                         "safer-buffer": ">= 2.1.2 < 3"
                     }
                 },
                 "iferr": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/iferr/-/iferr-1.0.2.tgz",
-                    "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg=="
+                    "bundled": true
                 },
                 "ignore-walk": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
-                    "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
+                    "bundled": true,
                     "requires": {
                         "minimatch": "^3.0.4"
                     }
                 },
                 "import-lazy": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
-                    "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
+                    "bundled": true
                 },
                 "imurmurhash": {
                     "version": "0.1.4",
-                    "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-                    "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+                    "bundled": true
                 },
                 "infer-owner": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
-                    "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="
+                    "bundled": true
                 },
                 "inflight": {
                     "version": "1.0.6",
-                    "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-                    "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+                    "bundled": true,
                     "requires": {
                         "once": "^1.3.0",
                         "wrappy": "1"
@@ -13769,18 +13618,15 @@
                 },
                 "inherits": {
                     "version": "2.0.4",
-                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-                    "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+                    "bundled": true
                 },
                 "ini": {
                     "version": "1.3.8",
-                    "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
-                    "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+                    "bundled": true
                 },
                 "init-package-json": {
                     "version": "1.10.3",
-                    "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz",
-                    "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==",
+                    "bundled": true,
                     "requires": {
                         "glob": "^7.1.1",
                         "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0",
@@ -13794,59 +13640,50 @@
                 },
                 "ip": {
                     "version": "1.1.5",
-                    "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
-                    "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+                    "bundled": true
                 },
                 "ip-regex": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
-                    "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
+                    "bundled": true
                 },
                 "is-callable": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
-                    "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="
+                    "bundled": true
                 },
                 "is-ci": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
-                    "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
+                    "bundled": true,
                     "requires": {
                         "ci-info": "^1.5.0"
                     },
                     "dependencies": {
                         "ci-info": {
                             "version": "1.6.0",
-                            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
-                            "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
+                            "bundled": true
                         }
                     }
                 },
                 "is-cidr": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.0.0.tgz",
-                    "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==",
+                    "bundled": true,
                     "requires": {
                         "cidr-regex": "^2.0.10"
                     }
                 },
                 "is-date-object": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
-                    "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
+                    "bundled": true
                 },
                 "is-fullwidth-code-point": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-                    "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+                    "bundled": true,
                     "requires": {
                         "number-is-nan": "^1.0.0"
                     }
                 },
                 "is-installed-globally": {
                     "version": "0.1.0",
-                    "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
-                    "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
+                    "bundled": true,
                     "requires": {
                         "global-dirs": "^0.1.0",
                         "is-path-inside": "^1.0.0"
@@ -13854,103 +13691,85 @@
                 },
                 "is-npm": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
-                    "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
+                    "bundled": true
                 },
                 "is-obj": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
-                    "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+                    "bundled": true
                 },
                 "is-path-inside": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
-                    "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+                    "bundled": true,
                     "requires": {
                         "path-is-inside": "^1.0.1"
                     }
                 },
                 "is-redirect": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
-                    "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
+                    "bundled": true
                 },
                 "is-regex": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
-                    "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+                    "bundled": true,
                     "requires": {
                         "has": "^1.0.1"
                     }
                 },
                 "is-retry-allowed": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
-                    "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
+                    "bundled": true
                 },
                 "is-stream": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-                    "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+                    "bundled": true
                 },
                 "is-symbol": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
-                    "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+                    "bundled": true,
                     "requires": {
                         "has-symbols": "^1.0.0"
                     }
                 },
                 "is-typedarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-                    "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+                    "bundled": true
                 },
                 "isarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-                    "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+                    "bundled": true
                 },
                 "isexe": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-                    "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+                    "bundled": true
                 },
                 "isstream": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-                    "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+                    "bundled": true
                 },
                 "jsbn": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-                    "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+                    "bundled": true,
                     "optional": true
                 },
                 "json-parse-better-errors": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-                    "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+                    "bundled": true
                 },
                 "json-schema": {
                     "version": "0.4.0",
-                    "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
-                    "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
+                    "bundled": true
                 },
                 "json-stringify-safe": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-                    "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+                    "bundled": true
                 },
                 "jsonparse": {
                     "version": "1.3.1",
-                    "resolved": false,
-                    "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+                    "bundled": true
                 },
                 "jsprim": {
                     "version": "1.4.2",
-                    "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
-                    "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "1.0.0",
                         "extsprintf": "1.3.0",
@@ -13960,21 +13779,18 @@
                 },
                 "latest-version": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
-                    "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
+                    "bundled": true,
                     "requires": {
                         "package-json": "^4.0.0"
                     }
                 },
                 "lazy-property": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz",
-                    "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc="
+                    "bundled": true
                 },
                 "libcipm": {
                     "version": "4.0.8",
-                    "resolved": "https://registry.npmjs.org/libcipm/-/libcipm-4.0.8.tgz",
-                    "integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==",
+                    "bundled": true,
                     "requires": {
                         "bin-links": "^1.1.2",
                         "bluebird": "^3.5.1",
@@ -13995,8 +13811,7 @@
                 },
                 "libnpm": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/libnpm/-/libnpm-3.0.1.tgz",
-                    "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==",
+                    "bundled": true,
                     "requires": {
                         "bin-links": "^1.1.2",
                         "bluebird": "^3.5.3",
@@ -14022,8 +13837,7 @@
                 },
                 "libnpmaccess": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.2.tgz",
-                    "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "get-stream": "^4.0.0",
@@ -14033,8 +13847,7 @@
                 },
                 "libnpmconfig": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz",
-                    "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1",
                         "find-up": "^3.0.0",
@@ -14043,16 +13856,14 @@
                     "dependencies": {
                         "find-up": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-                            "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+                            "bundled": true,
                             "requires": {
                                 "locate-path": "^3.0.0"
                             }
                         },
                         "locate-path": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-                            "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+                            "bundled": true,
                             "requires": {
                                 "p-locate": "^3.0.0",
                                 "path-exists": "^3.0.0"
@@ -14060,31 +13871,27 @@
                         },
                         "p-limit": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
-                            "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+                            "bundled": true,
                             "requires": {
                                 "p-try": "^2.0.0"
                             }
                         },
                         "p-locate": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-                            "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+                            "bundled": true,
                             "requires": {
                                 "p-limit": "^2.0.0"
                             }
                         },
                         "p-try": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-                            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+                            "bundled": true
                         }
                     }
                 },
                 "libnpmhook": {
                     "version": "5.0.3",
-                    "resolved": "https://registry.npmjs.org/libnpmhook/-/libnpmhook-5.0.3.tgz",
-                    "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.4.1",
@@ -14094,8 +13901,7 @@
                 },
                 "libnpmorg": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/libnpmorg/-/libnpmorg-1.0.1.tgz",
-                    "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.4.1",
@@ -14105,8 +13911,7 @@
                 },
                 "libnpmpublish": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-1.1.2.tgz",
-                    "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.5.1",
@@ -14121,8 +13926,7 @@
                 },
                 "libnpmsearch": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-2.0.2.tgz",
-                    "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1",
                         "get-stream": "^4.0.0",
@@ -14131,8 +13935,7 @@
                 },
                 "libnpmteam": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmteam/-/libnpmteam-1.0.2.tgz",
-                    "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.4.1",
@@ -14142,8 +13945,7 @@
                 },
                 "libnpx": {
                     "version": "10.2.4",
-                    "resolved": "https://registry.npmjs.org/libnpx/-/libnpx-10.2.4.tgz",
-                    "integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==",
+                    "bundled": true,
                     "requires": {
                         "dotenv": "^5.0.1",
                         "npm-package-arg": "^6.0.0",
@@ -14157,8 +13959,7 @@
                 },
                 "lock-verify": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.1.0.tgz",
-                    "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==",
+                    "bundled": true,
                     "requires": {
                         "npm-package-arg": "^6.1.0",
                         "semver": "^5.4.1"
@@ -14166,21 +13967,18 @@
                 },
                 "lockfile": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz",
-                    "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==",
+                    "bundled": true,
                     "requires": {
                         "signal-exit": "^3.0.2"
                     }
                 },
                 "lodash._baseindexof": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz",
-                    "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw="
+                    "bundled": true
                 },
                 "lodash._baseuniq": {
                     "version": "4.6.0",
-                    "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
-                    "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=",
+                    "bundled": true,
                     "requires": {
                         "lodash._createset": "~4.0.0",
                         "lodash._root": "~3.0.0"
@@ -14188,87 +13986,72 @@
                 },
                 "lodash._bindcallback": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
-                    "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4="
+                    "bundled": true
                 },
                 "lodash._cacheindexof": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz",
-                    "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI="
+                    "bundled": true
                 },
                 "lodash._createcache": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz",
-                    "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=",
+                    "bundled": true,
                     "requires": {
                         "lodash._getnative": "^3.0.0"
                     }
                 },
                 "lodash._createset": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
-                    "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY="
+                    "bundled": true
                 },
                 "lodash._getnative": {
                     "version": "3.9.1",
-                    "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-                    "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
+                    "bundled": true
                 },
                 "lodash._root": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
-                    "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI="
+                    "bundled": true
                 },
                 "lodash.clonedeep": {
                     "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
-                    "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
+                    "bundled": true
                 },
                 "lodash.restparam": {
                     "version": "3.6.1",
-                    "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
-                    "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU="
+                    "bundled": true
                 },
                 "lodash.union": {
                     "version": "4.6.0",
-                    "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
-                    "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
+                    "bundled": true
                 },
                 "lodash.uniq": {
                     "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-                    "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
+                    "bundled": true
                 },
                 "lodash.without": {
                     "version": "4.4.0",
-                    "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz",
-                    "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw="
+                    "bundled": true
                 },
                 "lowercase-keys": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
-                    "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
+                    "bundled": true
                 },
                 "lru-cache": {
                     "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-                    "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+                    "bundled": true,
                     "requires": {
                         "yallist": "^3.0.2"
                     }
                 },
                 "make-dir": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
-                    "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+                    "bundled": true,
                     "requires": {
                         "pify": "^3.0.0"
                     }
                 },
                 "make-fetch-happen": {
                     "version": "5.0.2",
-                    "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz",
-                    "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
+                    "bundled": true,
                     "requires": {
                         "agentkeepalive": "^3.4.1",
                         "cacache": "^12.0.0",
@@ -14285,47 +14068,40 @@
                 },
                 "meant": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.2.tgz",
-                    "integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg=="
+                    "bundled": true
                 },
                 "mime-db": {
                     "version": "1.35.0",
-                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
-                    "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="
+                    "bundled": true
                 },
                 "mime-types": {
                     "version": "2.1.19",
-                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
-                    "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
+                    "bundled": true,
                     "requires": {
                         "mime-db": "~1.35.0"
                     }
                 },
                 "minimatch": {
                     "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-                    "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+                    "bundled": true,
                     "requires": {
                         "brace-expansion": "^1.1.7"
                     }
                 },
                 "minimist": {
                     "version": "1.2.6",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
-                    "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+                    "bundled": true
                 },
                 "minizlib": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
-                    "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+                    "bundled": true,
                     "requires": {
                         "minipass": "^2.9.0"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -14335,8 +14111,7 @@
                 },
                 "mississippi": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
-                    "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+                    "bundled": true,
                     "requires": {
                         "concat-stream": "^1.5.0",
                         "duplexify": "^3.4.2",
@@ -14352,23 +14127,20 @@
                 },
                 "mkdirp": {
                     "version": "0.5.5",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-                    "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+                    "bundled": true,
                     "requires": {
                         "minimist": "^1.2.5"
                     },
                     "dependencies": {
                         "minimist": {
                             "version": "1.2.6",
-                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
-                            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+                            "bundled": true
                         }
                     }
                 },
                 "move-concurrently": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
-                    "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.1",
                         "copy-concurrently": "^1.0.0",
@@ -14380,25 +14152,21 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         }
                     }
                 },
                 "ms": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
-                    "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+                    "bundled": true
                 },
                 "mute-stream": {
                     "version": "0.0.7",
-                    "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
-                    "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
+                    "bundled": true
                 },
                 "node-fetch-npm": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz",
-                    "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==",
+                    "bundled": true,
                     "requires": {
                         "encoding": "^0.1.11",
                         "json-parse-better-errors": "^1.0.0",
@@ -14407,8 +14175,7 @@
                 },
                 "node-gyp": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz",
-                    "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==",
+                    "bundled": true,
                     "requires": {
                         "env-paths": "^2.2.0",
                         "glob": "^7.1.4",
@@ -14425,8 +14192,7 @@
                 },
                 "nopt": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
-                    "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
+                    "bundled": true,
                     "requires": {
                         "abbrev": "1",
                         "osenv": "^0.1.4"
@@ -14434,8 +14200,7 @@
                 },
                 "normalize-package-data": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
-                    "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+                    "bundled": true,
                     "requires": {
                         "hosted-git-info": "^2.1.4",
                         "resolve": "^1.10.0",
@@ -14445,8 +14210,7 @@
                     "dependencies": {
                         "resolve": {
                             "version": "1.10.0",
-                            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
-                            "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+                            "bundled": true,
                             "requires": {
                                 "path-parse": "^1.0.6"
                             }
@@ -14455,8 +14219,7 @@
                 },
                 "npm-audit-report": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-1.3.3.tgz",
-                    "integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==",
+                    "bundled": true,
                     "requires": {
                         "cli-table3": "^0.5.0",
                         "console-control-strings": "^1.1.0"
@@ -14464,29 +14227,25 @@
                 },
                 "npm-bundled": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
-                    "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
+                    "bundled": true,
                     "requires": {
                         "npm-normalize-package-bin": "^1.0.1"
                     }
                 },
                 "npm-cache-filename": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz",
-                    "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE="
+                    "bundled": true
                 },
                 "npm-install-checks": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.2.tgz",
-                    "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==",
+                    "bundled": true,
                     "requires": {
                         "semver": "^2.3.0 || 3.x || 4 || 5"
                     }
                 },
                 "npm-lifecycle": {
                     "version": "3.1.5",
-                    "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz",
-                    "integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==",
+                    "bundled": true,
                     "requires": {
                         "byline": "^5.0.0",
                         "graceful-fs": "^4.1.15",
@@ -14500,18 +14259,15 @@
                 },
                 "npm-logical-tree": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz",
-                    "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg=="
+                    "bundled": true
                 },
                 "npm-normalize-package-bin": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
-                    "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
+                    "bundled": true
                 },
                 "npm-package-arg": {
                     "version": "6.1.1",
-                    "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
-                    "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
+                    "bundled": true,
                     "requires": {
                         "hosted-git-info": "^2.7.1",
                         "osenv": "^0.1.5",
@@ -14521,8 +14277,7 @@
                 },
                 "npm-packlist": {
                     "version": "1.4.8",
-                    "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
-                    "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
+                    "bundled": true,
                     "requires": {
                         "ignore-walk": "^3.0.1",
                         "npm-bundled": "^1.0.1",
@@ -14531,8 +14286,7 @@
                 },
                 "npm-pick-manifest": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz",
-                    "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1",
                         "npm-package-arg": "^6.0.0",
@@ -14541,8 +14295,7 @@
                 },
                 "npm-profile": {
                     "version": "4.0.4",
-                    "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz",
-                    "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.2 || 2",
                         "figgy-pudding": "^3.4.1",
@@ -14551,8 +14304,7 @@
                 },
                 "npm-registry-fetch": {
                     "version": "4.0.7",
-                    "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz",
-                    "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==",
+                    "bundled": true,
                     "requires": {
                         "JSONStream": "^1.3.4",
                         "bluebird": "^3.5.1",
@@ -14565,28 +14317,24 @@
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.1",
-                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-                            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+                            "bundled": true
                         }
                     }
                 },
                 "npm-run-path": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-                    "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+                    "bundled": true,
                     "requires": {
                         "path-key": "^2.0.0"
                     }
                 },
                 "npm-user-validate": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-1.0.1.tgz",
-                    "integrity": "sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw=="
+                    "bundled": true
                 },
                 "npmlog": {
                     "version": "4.1.2",
-                    "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-                    "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+                    "bundled": true,
                     "requires": {
                         "are-we-there-yet": "~1.1.2",
                         "console-control-strings": "~1.1.0",
@@ -14596,28 +14344,23 @@
                 },
                 "number-is-nan": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-                    "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+                    "bundled": true
                 },
                 "oauth-sign": {
                     "version": "0.9.0",
-                    "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-                    "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
+                    "bundled": true
                 },
                 "object-assign": {
                     "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-                    "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+                    "bundled": true
                 },
                 "object-keys": {
                     "version": "1.0.12",
-                    "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
-                    "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
+                    "bundled": true
                 },
                 "object.getownpropertydescriptors": {
                     "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
-                    "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
+                    "bundled": true,
                     "requires": {
                         "define-properties": "^1.1.2",
                         "es-abstract": "^1.5.1"
@@ -14625,31 +14368,26 @@
                 },
                 "once": {
                     "version": "1.4.0",
-                    "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-                    "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+                    "bundled": true,
                     "requires": {
                         "wrappy": "1"
                     }
                 },
                 "opener": {
                     "version": "1.5.2",
-                    "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
-                    "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="
+                    "bundled": true
                 },
                 "os-homedir": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-                    "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+                    "bundled": true
                 },
                 "os-tmpdir": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-                    "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+                    "bundled": true
                 },
                 "osenv": {
                     "version": "0.1.5",
-                    "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-                    "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+                    "bundled": true,
                     "requires": {
                         "os-homedir": "^1.0.0",
                         "os-tmpdir": "^1.0.0"
@@ -14657,13 +14395,11 @@
                 },
                 "p-finally": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-                    "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+                    "bundled": true
                 },
                 "package-json": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
-                    "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
+                    "bundled": true,
                     "requires": {
                         "got": "^6.7.1",
                         "registry-auth-token": "^3.0.1",
@@ -14673,8 +14409,7 @@
                 },
                 "pacote": {
                     "version": "9.5.12",
-                    "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz",
-                    "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==",
+                    "bundled": true,
                     "requires": {
                         "bluebird": "^3.5.3",
                         "cacache": "^12.0.2",
@@ -14710,8 +14445,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -14721,8 +14455,7 @@
                 },
                 "parallel-transform": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
-                    "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
+                    "bundled": true,
                     "requires": {
                         "cyclist": "~0.2.2",
                         "inherits": "^2.0.3",
@@ -14731,8 +14464,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -14745,8 +14477,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -14755,58 +14486,47 @@
                 },
                 "path-exists": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-                    "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+                    "bundled": true
                 },
                 "path-is-absolute": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-                    "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+                    "bundled": true
                 },
                 "path-is-inside": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-                    "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
+                    "bundled": true
                 },
                 "path-key": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-                    "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+                    "bundled": true
                 },
                 "path-parse": {
                     "version": "1.0.7",
-                    "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-                    "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+                    "bundled": true
                 },
                 "performance-now": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-                    "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+                    "bundled": true
                 },
                 "pify": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+                    "bundled": true
                 },
                 "prepend-http": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-                    "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
+                    "bundled": true
                 },
                 "process-nextick-args": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-                    "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
+                    "bundled": true
                 },
                 "promise-inflight": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-                    "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
+                    "bundled": true
                 },
                 "promise-retry": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
-                    "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
+                    "bundled": true,
                     "requires": {
                         "err-code": "^1.0.0",
                         "retry": "^0.10.0"
@@ -14814,51 +14534,43 @@
                     "dependencies": {
                         "retry": {
                             "version": "0.10.1",
-                            "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
-                            "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
+                            "bundled": true
                         }
                     }
                 },
                 "promzard": {
                     "version": "0.3.0",
-                    "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz",
-                    "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=",
+                    "bundled": true,
                     "requires": {
                         "read": "1"
                     }
                 },
                 "proto-list": {
                     "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
-                    "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
+                    "bundled": true
                 },
                 "protoduck": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
-                    "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
+                    "bundled": true,
                     "requires": {
                         "genfun": "^5.0.0"
                     }
                 },
                 "prr": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-                    "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
+                    "bundled": true
                 },
                 "pseudomap": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-                    "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+                    "bundled": true
                 },
                 "psl": {
                     "version": "1.1.29",
-                    "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
-                    "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
+                    "bundled": true
                 },
                 "pump": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
-                    "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+                    "bundled": true,
                     "requires": {
                         "end-of-stream": "^1.1.0",
                         "once": "^1.3.1"
@@ -14866,8 +14578,7 @@
                 },
                 "pumpify": {
                     "version": "1.5.1",
-                    "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
-                    "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+                    "bundled": true,
                     "requires": {
                         "duplexify": "^3.6.0",
                         "inherits": "^2.0.3",
@@ -14876,8 +14587,7 @@
                     "dependencies": {
                         "pump": {
                             "version": "2.0.1",
-                            "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
-                            "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+                            "bundled": true,
                             "requires": {
                                 "end-of-stream": "^1.1.0",
                                 "once": "^1.3.1"
@@ -14887,23 +14597,19 @@
                 },
                 "punycode": {
                     "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-                    "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+                    "bundled": true
                 },
                 "qrcode-terminal": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz",
-                    "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="
+                    "bundled": true
                 },
                 "qs": {
                     "version": "6.5.2",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-                    "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+                    "bundled": true
                 },
                 "query-string": {
                     "version": "6.8.2",
-                    "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.2.tgz",
-                    "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==",
+                    "bundled": true,
                     "requires": {
                         "decode-uri-component": "^0.2.0",
                         "split-on-first": "^1.0.0",
@@ -14912,13 +14618,11 @@
                 },
                 "qw": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.1.tgz",
-                    "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ="
+                    "bundled": true
                 },
                 "rc": {
                     "version": "1.2.8",
-                    "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
-                    "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+                    "bundled": true,
                     "requires": {
                         "deep-extend": "^0.6.0",
                         "ini": "~1.3.0",
@@ -14928,24 +14632,21 @@
                 },
                 "read": {
                     "version": "1.0.7",
-                    "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
-                    "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
+                    "bundled": true,
                     "requires": {
                         "mute-stream": "~0.0.4"
                     }
                 },
                 "read-cmd-shim": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz",
-                    "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2"
                     }
                 },
                 "read-installed": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz",
-                    "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=",
+                    "bundled": true,
                     "requires": {
                         "debuglog": "^1.0.1",
                         "graceful-fs": "^4.1.2",
@@ -14958,8 +14659,7 @@
                 },
                 "read-package-json": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz",
-                    "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
+                    "bundled": true,
                     "requires": {
                         "glob": "^7.1.1",
                         "graceful-fs": "^4.1.2",
@@ -14970,8 +14670,7 @@
                 },
                 "read-package-tree": {
                     "version": "5.3.1",
-                    "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz",
-                    "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
+                    "bundled": true,
                     "requires": {
                         "read-package-json": "^2.0.0",
                         "readdir-scoped-modules": "^1.0.0",
@@ -14980,8 +14679,7 @@
                 },
                 "readable-stream": {
                     "version": "3.6.0",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
-                    "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+                    "bundled": true,
                     "requires": {
                         "inherits": "^2.0.3",
                         "string_decoder": "^1.1.1",
@@ -14990,8 +14688,7 @@
                 },
                 "readdir-scoped-modules": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
-                    "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
+                    "bundled": true,
                     "requires": {
                         "debuglog": "^1.0.1",
                         "dezalgo": "^1.0.0",
@@ -15001,8 +14698,7 @@
                 },
                 "registry-auth-token": {
                     "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
-                    "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
+                    "bundled": true,
                     "requires": {
                         "rc": "^1.1.6",
                         "safe-buffer": "^5.0.1"
@@ -15010,16 +14706,14 @@
                 },
                 "registry-url": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
-                    "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+                    "bundled": true,
                     "requires": {
                         "rc": "^1.0.1"
                     }
                 },
                 "request": {
                     "version": "2.88.0",
-                    "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-                    "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+                    "bundled": true,
                     "requires": {
                         "aws-sign2": "~0.7.0",
                         "aws4": "^1.8.0",
@@ -15045,115 +14739,96 @@
                 },
                 "require-directory": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-                    "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+                    "bundled": true
                 },
                 "require-main-filename": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-                    "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+                    "bundled": true
                 },
                 "resolve-from": {
                     "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-                    "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
+                    "bundled": true
                 },
                 "retry": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
-                    "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
+                    "bundled": true
                 },
                 "rimraf": {
                     "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+                    "bundled": true,
                     "requires": {
                         "glob": "^7.1.3"
                     }
                 },
                 "run-queue": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
-                    "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.1"
                     },
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         }
                     }
                 },
                 "safe-buffer": {
                     "version": "5.1.2",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-                    "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+                    "bundled": true
                 },
                 "safer-buffer": {
                     "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-                    "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+                    "bundled": true
                 },
                 "semver": {
                     "version": "5.7.1",
-                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+                    "bundled": true
                 },
                 "semver-diff": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
-                    "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+                    "bundled": true,
                     "requires": {
                         "semver": "^5.0.3"
                     }
                 },
                 "set-blocking": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-                    "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+                    "bundled": true
                 },
                 "sha": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/sha/-/sha-3.0.0.tgz",
-                    "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2"
                     }
                 },
                 "shebang-command": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-                    "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+                    "bundled": true,
                     "requires": {
                         "shebang-regex": "^1.0.0"
                     }
                 },
                 "shebang-regex": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-                    "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+                    "bundled": true
                 },
                 "signal-exit": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-                    "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+                    "bundled": true
                 },
                 "slide": {
                     "version": "1.1.6",
-                    "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
-                    "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
+                    "bundled": true
                 },
                 "smart-buffer": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
-                    "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw=="
+                    "bundled": true
                 },
                 "socks": {
                     "version": "2.3.3",
-                    "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
-                    "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
+                    "bundled": true,
                     "requires": {
                         "ip": "1.1.5",
                         "smart-buffer": "^4.1.0"
@@ -15161,8 +14836,7 @@
                 },
                 "socks-proxy-agent": {
                     "version": "4.0.2",
-                    "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz",
-                    "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
+                    "bundled": true,
                     "requires": {
                         "agent-base": "~4.2.1",
                         "socks": "~2.3.2"
@@ -15170,8 +14844,7 @@
                     "dependencies": {
                         "agent-base": {
                             "version": "4.2.1",
-                            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
-                            "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+                            "bundled": true,
                             "requires": {
                                 "es6-promisify": "^5.0.0"
                             }
@@ -15180,13 +14853,11 @@
                 },
                 "sorted-object": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz",
-                    "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw="
+                    "bundled": true
                 },
                 "sorted-union-stream": {
                     "version": "2.1.3",
-                    "resolved": "https://registry.npmjs.org/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz",
-                    "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=",
+                    "bundled": true,
                     "requires": {
                         "from2": "^1.3.0",
                         "stream-iterate": "^1.1.0"
@@ -15194,8 +14865,7 @@
                     "dependencies": {
                         "from2": {
                             "version": "1.3.0",
-                            "resolved": "https://registry.npmjs.org/from2/-/from2-1.3.0.tgz",
-                            "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=",
+                            "bundled": true,
                             "requires": {
                                 "inherits": "~2.0.1",
                                 "readable-stream": "~1.1.10"
@@ -15203,13 +14873,11 @@
                         },
                         "isarray": {
                             "version": "0.0.1",
-                            "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-                            "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+                            "bundled": true
                         },
                         "readable-stream": {
                             "version": "1.1.14",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-                            "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.1",
@@ -15219,15 +14887,13 @@
                         },
                         "string_decoder": {
                             "version": "0.10.31",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-                            "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+                            "bundled": true
                         }
                     }
                 },
                 "spdx-correct": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
-                    "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
+                    "bundled": true,
                     "requires": {
                         "spdx-expression-parse": "^3.0.0",
                         "spdx-license-ids": "^3.0.0"
@@ -15235,13 +14901,11 @@
                 },
                 "spdx-exceptions": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
-                    "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
+                    "bundled": true
                 },
                 "spdx-expression-parse": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-                    "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+                    "bundled": true,
                     "requires": {
                         "spdx-exceptions": "^2.1.0",
                         "spdx-license-ids": "^3.0.0"
@@ -15249,18 +14913,15 @@
                 },
                 "spdx-license-ids": {
                     "version": "3.0.5",
-                    "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
-                    "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
+                    "bundled": true
                 },
                 "split-on-first": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
-                    "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+                    "bundled": true
                 },
                 "sshpk": {
                     "version": "1.14.2",
-                    "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
-                    "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
+                    "bundled": true,
                     "requires": {
                         "asn1": "~0.2.3",
                         "assert-plus": "^1.0.0",
@@ -15275,16 +14936,14 @@
                 },
                 "ssri": {
                     "version": "6.0.2",
-                    "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
-                    "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1"
                     }
                 },
                 "stream-each": {
                     "version": "1.2.2",
-                    "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz",
-                    "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==",
+                    "bundled": true,
                     "requires": {
                         "end-of-stream": "^1.1.0",
                         "stream-shift": "^1.0.0"
@@ -15292,8 +14951,7 @@
                 },
                 "stream-iterate": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/stream-iterate/-/stream-iterate-1.2.0.tgz",
-                    "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=",
+                    "bundled": true,
                     "requires": {
                         "readable-stream": "^2.1.5",
                         "stream-shift": "^1.0.0"
@@ -15301,8 +14959,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -15315,8 +14972,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -15325,18 +14981,15 @@
                 },
                 "stream-shift": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
-                    "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
+                    "bundled": true
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-                    "resolved": false,
-                    "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
+                    "bundled": true
                 },
                 "string-width": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-                    "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+                    "bundled": true,
                     "requires": {
                         "is-fullwidth-code-point": "^2.0.0",
                         "strip-ansi": "^4.0.0"
@@ -15344,18 +14997,15 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-                            "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+                            "bundled": true
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "strip-ansi": {
                             "version": "4.0.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-                            "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^3.0.0"
                             }
@@ -15377,39 +15027,33 @@
                 },
                 "stringify-package": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz",
-                    "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg=="
+                    "bundled": true
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+                    "bundled": true,
                     "requires": {
                         "ansi-regex": "^2.0.0"
                     }
                 },
                 "strip-eof": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-                    "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+                    "bundled": true
                 },
                 "strip-json-comments": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-                    "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+                    "bundled": true
                 },
                 "supports-color": {
                     "version": "5.4.0",
-                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-                    "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+                    "bundled": true,
                     "requires": {
                         "has-flag": "^3.0.0"
                     }
                 },
                 "tar": {
                     "version": "4.4.19",
-                    "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
-                    "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
+                    "bundled": true,
                     "requires": {
                         "chownr": "^1.1.4",
                         "fs-minipass": "^1.2.7",
@@ -15422,8 +15066,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -15431,38 +15074,32 @@
                         },
                         "safe-buffer": {
                             "version": "5.2.1",
-                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-                            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+                            "bundled": true
                         },
                         "yallist": {
                             "version": "3.1.1",
-                            "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-                            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+                            "bundled": true
                         }
                     }
                 },
                 "term-size": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
-                    "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+                    "bundled": true,
                     "requires": {
                         "execa": "^0.7.0"
                     }
                 },
                 "text-table": {
                     "version": "0.2.0",
-                    "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-                    "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
+                    "bundled": true
                 },
                 "through": {
                     "version": "2.3.8",
-                    "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-                    "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+                    "bundled": true
                 },
                 "through2": {
                     "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
-                    "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+                    "bundled": true,
                     "requires": {
                         "readable-stream": "^2.1.5",
                         "xtend": "~4.0.1"
@@ -15470,8 +15107,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -15484,8 +15120,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
                             }
@@ -15494,18 +15129,15 @@
                 },
                 "timed-out": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
-                    "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
+                    "bundled": true
                 },
                 "tiny-relative-date": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz",
-                    "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A=="
+                    "bundled": true
                 },
                 "tough-cookie": {
                     "version": "2.4.3",
-                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-                    "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+                    "bundled": true,
                     "requires": {
                         "psl": "^1.1.24",
                         "punycode": "^1.4.1"
@@ -15513,71 +15145,60 @@
                 },
                 "tunnel-agent": {
                     "version": "0.6.0",
-                    "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-                    "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+                    "bundled": true,
                     "requires": {
                         "safe-buffer": "^5.0.1"
                     }
                 },
                 "tweetnacl": {
                     "version": "0.14.5",
-                    "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-                    "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+                    "bundled": true,
                     "optional": true
                 },
                 "typedarray": {
                     "version": "0.0.6",
-                    "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-                    "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+                    "bundled": true
                 },
                 "uid-number": {
                     "version": "0.0.6",
-                    "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
-                    "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE="
+                    "bundled": true
                 },
                 "umask": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz",
-                    "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0="
+                    "bundled": true
                 },
                 "unique-filename": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
-                    "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+                    "bundled": true,
                     "requires": {
                         "unique-slug": "^2.0.0"
                     }
                 },
                 "unique-slug": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
-                    "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
+                    "bundled": true,
                     "requires": {
                         "imurmurhash": "^0.1.4"
                     }
                 },
                 "unique-string": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
-                    "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+                    "bundled": true,
                     "requires": {
                         "crypto-random-string": "^1.0.0"
                     }
                 },
                 "unpipe": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-                    "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+                    "bundled": true
                 },
                 "unzip-response": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
-                    "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
+                    "bundled": true
                 },
                 "update-notifier": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
-                    "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
+                    "bundled": true,
                     "requires": {
                         "boxen": "^1.2.1",
                         "chalk": "^2.0.1",
@@ -15593,54 +15214,46 @@
                 },
                 "uri-js": {
                     "version": "4.4.0",
-                    "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
-                    "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+                    "bundled": true,
                     "requires": {
                         "punycode": "^2.1.0"
                     },
                     "dependencies": {
                         "punycode": {
                             "version": "2.1.1",
-                            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-                            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+                            "bundled": true
                         }
                     }
                 },
                 "url-parse-lax": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
-                    "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+                    "bundled": true,
                     "requires": {
                         "prepend-http": "^1.0.1"
                     }
                 },
                 "util-deprecate": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-                    "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+                    "bundled": true
                 },
                 "util-extend": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
-                    "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8="
+                    "bundled": true
                 },
                 "util-promisify": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz",
-                    "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
+                    "bundled": true,
                     "requires": {
                         "object.getownpropertydescriptors": "^2.0.3"
                     }
                 },
                 "uuid": {
                     "version": "3.3.3",
-                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
-                    "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
+                    "bundled": true
                 },
                 "validate-npm-package-license": {
                     "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-                    "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+                    "bundled": true,
                     "requires": {
                         "spdx-correct": "^3.0.0",
                         "spdx-expression-parse": "^3.0.0"
@@ -15648,16 +15261,14 @@
                 },
                 "validate-npm-package-name": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
-                    "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
+                    "bundled": true,
                     "requires": {
                         "builtins": "^1.0.3"
                     }
                 },
                 "verror": {
                     "version": "1.10.0",
-                    "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-                    "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0",
                         "core-util-is": "1.0.2",
@@ -15666,37 +15277,32 @@
                 },
                 "wcwidth": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
-                    "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
+                    "bundled": true,
                     "requires": {
                         "defaults": "^1.0.3"
                     }
                 },
                 "which": {
                     "version": "1.3.1",
-                    "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
-                    "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+                    "bundled": true,
                     "requires": {
                         "isexe": "^2.0.0"
                     }
                 },
                 "which-module": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-                    "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+                    "bundled": true
                 },
                 "wide-align": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
-                    "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^1.0.2"
                     },
                     "dependencies": {
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                            "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                            "bundled": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -15707,24 +15313,21 @@
                 },
                 "widest-line": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
-                    "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^2.1.1"
                     }
                 },
                 "worker-farm": {
                     "version": "1.7.0",
-                    "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
-                    "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+                    "bundled": true,
                     "requires": {
                         "errno": "~0.1.7"
                     }
                 },
                 "wrap-ansi": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-                    "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+                    "bundled": true,
                     "requires": {
                         "ansi-styles": "^3.2.0",
                         "string-width": "^3.0.0",
@@ -15733,18 +15336,15 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "4.1.1",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                            "bundled": true
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "3.1.0",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-                            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                            "bundled": true,
                             "requires": {
                                 "emoji-regex": "^7.0.1",
                                 "is-fullwidth-code-point": "^2.0.0",
@@ -15753,8 +15353,7 @@
                         },
                         "strip-ansi": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-                            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^4.1.0"
                             }
@@ -15763,13 +15362,11 @@
                 },
                 "wrappy": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-                    "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+                    "bundled": true
                 },
                 "write-file-atomic": {
                     "version": "2.4.3",
-                    "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
-                    "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.11",
                         "imurmurhash": "^0.1.4",
@@ -15778,28 +15375,23 @@
                 },
                 "xdg-basedir": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
-                    "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
+                    "bundled": true
                 },
                 "xtend": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
-                    "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+                    "bundled": true
                 },
                 "y18n": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
-                    "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ=="
+                    "bundled": true
                 },
                 "yallist": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
-                    "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
+                    "bundled": true
                 },
                 "yargs": {
                     "version": "14.2.3",
-                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
-                    "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+                    "bundled": true,
                     "requires": {
                         "cliui": "^5.0.0",
                         "decamelize": "^1.2.0",
@@ -15816,26 +15408,22 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "4.1.0",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+                            "bundled": true
                         },
                         "find-up": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-                            "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+                            "bundled": true,
                             "requires": {
                                 "locate-path": "^3.0.0"
                             }
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "locate-path": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-                            "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+                            "bundled": true,
                             "requires": {
                                 "p-locate": "^3.0.0",
                                 "path-exists": "^3.0.0"
@@ -15843,29 +15431,25 @@
                         },
                         "p-limit": {
                             "version": "2.3.0",
-                            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-                            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+                            "bundled": true,
                             "requires": {
                                 "p-try": "^2.0.0"
                             }
                         },
                         "p-locate": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-                            "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+                            "bundled": true,
                             "requires": {
                                 "p-limit": "^2.0.0"
                             }
                         },
                         "p-try": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-                            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "3.1.0",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-                            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                            "bundled": true,
                             "requires": {
                                 "emoji-regex": "^7.0.1",
                                 "is-fullwidth-code-point": "^2.0.0",
@@ -15874,8 +15458,7 @@
                         },
                         "strip-ansi": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-                            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^4.1.0"
                             }
@@ -15884,8 +15467,7 @@
                 },
                 "yargs-parser": {
                     "version": "15.0.1",
-                    "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
-                    "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+                    "bundled": true,
                     "requires": {
                         "camelcase": "^5.0.0",
                         "decamelize": "^1.2.0"
@@ -15893,8 +15475,7 @@
                     "dependencies": {
                         "camelcase": {
                             "version": "5.3.1",
-                            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-                            "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+                            "bundled": true
                         }
                     }
                 }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 02bc55e7a..bcfa695f0 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -88,11 +88,10 @@ export class CollectionSchemaView extends CollectionSubView() {
             this._selectedDocs.add(doc);
             this._lastSelectedRow = index;
         }
-        
+
         if (this._lastSelectedRow && this._selectedDocs.size > 0) {
             SelectionManager.SelectSchemaViewDoc(this.childDocs[this._lastSelectedRow]);
-        }
-        else {
+        } else {
             SelectionManager.SelectSchemaViewDoc(undefined);
         }
     };
@@ -154,7 +153,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
-        const newDoc = Docs.Create.TextDocument(value, {title: value});
+        const newDoc = Docs.Create.TextDocument(value, { title: value });
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
         return this.props.addDocument?.(newDoc) || false;
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 9bd1b843f..b9dbf4f57 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,9 +1,8 @@
 import React = require('react');
+import { computed } from 'mobx';
 import { observer } from 'mobx-react';
-import './CollectionSchemaView.scss';
 import { EditableView } from '../../EditableView';
-import { emptyFunction } from '../../../../Utils';
-import { action, computed } from 'mobx';
+import './CollectionSchemaView.scss';
 
 export interface SchemaColumnHeaderProps {
     columnKeys: string[];
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index ea2ebed7a..3d1fa0ee2 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -36,12 +36,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
         e.stopPropagation();
 
         setupMoveUpEvents(
-            this, e,
-            (e) => this.props.startDrag(e, this.props.Document),
+            this,
+            e,
+            e => this.props.startDrag(e, this.props.Document),
             emptyFunction,
-            (e) => this.props.selectRow(e, this.props.Document, this.props.rowIndex)
-        )
-    }
+            e => this.props.selectRow(e, this.props.Document, this.props.rowIndex)
+        );
+    };
 
     onPointerEnter = (e: any) => {
         if (!this.props.dragging) return;
@@ -54,7 +55,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
         let dragIsRow: boolean = true;
         DragManager.docsBeingDragged.forEach(doc => {
             dragIsRow = this.props.selectedRows.has(doc);
-        })
+        });
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
             const y = e.clientY - rect.top; //y position within the element.
@@ -82,17 +83,35 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
 
     render() {
         return (
-            <div className="schema-row" style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1} : {}} onPointerDown={this.onRowPointerDown} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} ref={(row: HTMLDivElement | null) => (this._ref = row)}>
-                <div className="row-menu" style={{width: this.props.rowMenuWidth}}>
-                    <div className="row-button" onPointerDown={undoBatch((e) => {e.stopPropagation(); this.props.removeDocument?.(this.props.Document)})}>
+            <div
+                className="schema-row"
+                style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
+                onPointerDown={this.onRowPointerDown}
+                onPointerEnter={this.onPointerEnter}
+                onPointerLeave={this.onPointerLeave}
+                ref={(row: HTMLDivElement | null) => (this._ref = row)}>
+                <div className="row-menu" style={{ width: this.props.rowMenuWidth }}>
+                    <div
+                        className="row-button"
+                        onPointerDown={undoBatch(e => {
+                            e.stopPropagation();
+                            this.props.removeDocument?.(this.props.Document);
+                        })}>
                         <FontAwesomeIcon icon="times" />
                     </div>
-                    <div className="row-button" onPointerDown={(e) => {e.stopPropagation(); this.props.addDocTab(this.props.Document, 'add:right')}}>
+                    <div
+                        className="row-button"
+                        onPointerDown={e => {
+                            e.stopPropagation();
+                            this.props.addDocTab(this.props.Document, 'add:right');
+                        }}>
                         <FontAwesomeIcon icon="external-link-alt" />
                     </div>
                 </div>
                 <div className="row-cells">
-                    {this.props.columnKeys.map((key, index) => (<SchemaTableCell Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />))}
+                    {this.props.columnKeys.map((key, index) => (
+                        <SchemaTableCell Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+                    ))}
                 </div>
             </div>
         );
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 1eb5e0e01..7e2fa1f6f 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,7 +1,6 @@
 import React = require('react');
 import { observer } from 'mobx-react';
 import { Doc, Field } from '../../../../fields/Doc';
-import { StrCast } from '../../../../fields/Types';
 import './CollectionSchemaView.scss';
 
 export interface SchemaTableCellProps {
-- 
cgit v1.2.3-70-g09d2


From 330752fa253b61136b78ce7147e09e9d6004f833 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 18 Aug 2022 14:08:31 -0400
Subject: added external drop and column resize

---
 .../collectionSchema/CollectionSchemaView.scss     |  32 ++++++-
 .../collectionSchema/CollectionSchemaView.tsx      | 105 ++++++++++++++++++---
 .../collectionSchema/SchemaColumnHeader.tsx        |   8 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   6 +-
 4 files changed, 133 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 83164a82a..1d0ab459d 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -11,11 +11,12 @@
         .schema-row {
             display: flex;
             flex-direction: row;
+            max-height: 70px;
+            overflow: auto;
 
             .schema-column-header,
             .schema-table-cell,
             .row-menu {
-                min-width: 50px;
                 border: 1px solid $medium-gray;
                 padding: 5px;
                 overflow: hidden;
@@ -31,6 +32,34 @@
                 flex-direction: row;
                 justify-content: space-between;
                 align-items: center;
+                padding: 0;
+
+                .schema-column-title {
+                    flex-grow: 2;
+                    margin: 5px;
+                }
+
+                .schema-header-menu {
+                    margin: 5px;
+                }
+
+                .schema-column-resizer {
+                    height: 100%;
+                    width: 3px;
+                    cursor: ew-resize;
+
+                    &:hover {
+                        background-color: $light-blue;
+                    }
+                }
+
+                .schema-column-resizer.right {
+                    align-self: flex-end;
+                }
+
+                .schema-column-resizer.left {
+                    align-self: flex-start;
+                }
             }
         }
 
@@ -41,6 +70,7 @@
                 display: flex;
                 flex-direction: row;
                 justify-content: center;
+                min-width: 50px;
             }
 
             .row-cells {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 4d96d5f7e..a09d2722c 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -2,25 +2,25 @@ import React = require('react');
 import { action, computed, observable, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { Cast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../../Utils';
+import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
-import { CollectionSubView } from '../CollectionSubView';
-import './CollectionSchemaView.scss';
-import { SchemaColumnHeader } from './SchemaColumnHeader';
-import { SchemaRowBox } from './SchemaRowBox';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch } from '../../../util/UndoManager';
-import { EditableView } from '../../EditableView';
-import { returnEmptyString } from '../../../../Utils';
-import { Docs, DocUtils } from '../../../documents/Documents';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
-import { Id } from '../../../../fields/FieldSymbols';
-import { RichTextField } from '../../../../fields/RichTextField';
-import { ImageField } from '../../../../fields/URLField';
+import { EditableView } from '../../EditableView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { CollectionSubView } from '../CollectionSubView';
+import './CollectionSchemaView.scss';
+import { SchemaColumnHeader } from './SchemaColumnHeader';
+import { SchemaRowBox } from './SchemaRowBox';
 
 export enum ColumnType {
     Number,
@@ -38,16 +38,18 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _lastSelectedRow: number | undefined;
     private _selectedDocSortedArray: Doc[] = [];
     private _closestDropIndex: number = 0;
+    private _minColWidth: number = 120;
 
     @observable _rowMenuWidth: number = 100;
     @observable _selectedDocs: ObservableSet = new ObservableSet<SchemaRowBox>();
     @observable _isDragging: boolean = false;
+    @observable _displayColumnWidths: number[] | undefined;
 
     @computed get columnKeys() {
         return Cast(this.layoutDoc.columnKeys, listSpec('string'), defaultColumnKeys);
     }
 
-    @computed get columnWidths() {
+    @computed get storedColumnWidths() {
         return Cast(
             this.layoutDoc.columnWidths,
             listSpec('number'),
@@ -55,6 +57,10 @@ export class CollectionSchemaView extends CollectionSubView() {
         );
     }
 
+    @computed get displayColumnWidths() {
+        return this._displayColumnWidths ?? this.storedColumnWidths;
+    }
+
     @undoBatch
     @action
     changeColumnKey = (index: number, newKey: string) => {
@@ -80,6 +86,51 @@ export class CollectionSchemaView extends CollectionSubView() {
         this.layoutDoc.columnKeys = new List<string>(currKeys);
     };
 
+    @action
+    startResize = (e: any, index: number, left: boolean) => {
+        this._displayColumnWidths = this.storedColumnWidths;
+        setupMoveUpEvents(this, e, (e, delta) => this.resizeColumn(e, index, left), this.finishResize, emptyFunction);
+    };
+
+    @action
+    resizeColumn = (e: PointerEvent, index: number, left: boolean) => {
+        if (this._displayColumnWidths) {
+            let shrinking;
+            let growing;
+
+            let change = e.movementX;
+
+            if (left && index !== 0) {
+                growing = change < 0 ? index : index - 1;
+                shrinking = change < 0 ? index - 1 : index;
+            } else if (!left && index !== this.columnKeys.length - 1) {
+                growing = change > 0 ? index : index + 1;
+                shrinking = change > 0 ? index + 1 : index;
+            }
+
+            if (shrinking === undefined || growing === undefined) return true;
+
+            change = Math.abs(change);
+            if (this._displayColumnWidths[shrinking] - change < this._minColWidth) {
+                change = this._displayColumnWidths[shrinking] - this._minColWidth;
+            }
+
+            this._displayColumnWidths[shrinking] -= change;
+            this._displayColumnWidths[growing] += change;
+
+            return false;
+        }
+        return true;
+    };
+
+    // @undoBatch
+    @action
+    finishResize = () => {
+        console.log('finished');
+        this.layoutDoc.columnWidths = new List<number>(this._displayColumnWidths);
+        this._displayColumnWidths = undefined;
+    };
+
     @action
     selectRow = (e: React.PointerEvent, doc: Doc, index: number) => {
         const ctrl = e.ctrlKey || e.metaKey;
@@ -135,6 +186,21 @@ export class CollectionSchemaView extends CollectionSubView() {
         return false;
     };
 
+    @action
+    onExternalDrop = async (e: React.DragEvent): Promise<void> => {
+        console.log('hello');
+        super.onExternalDrop(
+            e,
+            {},
+            undoBatch(
+                action(docus => {
+                    this._isDragging = false;
+                    docus.map((doc: Doc) => this.addDocument(doc));
+                })
+            )
+        );
+    };
+
     @action
     startDrag = (e: React.PointerEvent, doc: Doc) => {
         if (!this._selectedDocs.has(doc)) {
@@ -251,11 +317,20 @@ export class CollectionSchemaView extends CollectionSubView() {
                     this._ref = ele;
                     this.createDashEventsTarget(ele);
                 }}
-                onPointerDown={() => this._selectedDocs.clear()}>
+                onPointerDown={() => this._selectedDocs.clear()}
+                onDrop={this.onExternalDrop.bind(this)}>
                 <div className="schema-table">
                     <div className="schema-header-row">
                         {this.columnKeys.map((key, index) => (
-                            <SchemaColumnHeader columnIndex={index} columnKeys={this.columnKeys} columnWidths={this.columnWidths} changeColumnKey={this.changeColumnKey} addColumn={this.addColumn} removeColumn={this.removeColumn} />
+                            <SchemaColumnHeader
+                                columnIndex={index}
+                                columnKeys={this.columnKeys}
+                                columnWidths={this.displayColumnWidths}
+                                changeColumnKey={this.changeColumnKey}
+                                addColumn={this.addColumn}
+                                removeColumn={this.removeColumn}
+                                resizeColumn={this.startResize}
+                            />
                         ))}
                     </div>
                     <div className="schema-table-content">
@@ -267,7 +342,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 ContainingCollectionView={this.props.CollectionView}
                                 rowIndex={index}
                                 columnKeys={this.columnKeys}
-                                columnWidths={this.columnWidths}
+                                columnWidths={this.displayColumnWidths}
                                 rowMenuWidth={this._rowMenuWidth}
                                 selectedRows={this._selectedDocs}
                                 selectRow={this.selectRow}
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index f93a3d13d..bee76bb24 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -12,6 +12,7 @@ export interface SchemaColumnHeaderProps {
     changeColumnKey: (index: number, newKey: string) => boolean;
     addColumn: (index: number) => void;
     removeColumn: (index: number) => void;
+    resizeColumn: (e: any, index: number, left: boolean) => void;
 }
 
 @observer
@@ -23,7 +24,10 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
     render() {
         return (
             <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }}>
-                <EditableView SetValue={(newKey: string) => this.props.changeColumnKey(this.props.columnIndex, newKey)} GetValue={() => this.fieldKey} contents={this.fieldKey} />
+                <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex, true)}></div>
+                <div className="schema-column-title">
+                    <EditableView SetValue={(newKey: string) => this.props.changeColumnKey(this.props.columnIndex, newKey)} GetValue={() => this.fieldKey} contents={this.fieldKey} />
+                </div>
 
                 <div className="schema-header-menu">
                     <div
@@ -41,6 +45,8 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
                         <FontAwesomeIcon icon="trash" />
                     </div>
                 </div>
+
+                <div className="schema-column-resizer right" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex, false)}></div>
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 3d1fa0ee2..661056553 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -6,7 +6,7 @@ import { Doc } from '../../../../fields/Doc';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
-import { FieldViewProps } from '../../nodes/FieldView';
+import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
 import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
@@ -26,6 +26,10 @@ export interface SchemaRowBoxProps extends FieldViewProps {
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(SchemaRowBox, fieldKey);
+    }
+
     private _ref: HTMLDivElement | null = null;
 
     isSelected = () => this.props.selectedRows.has(this.props.Document);
-- 
cgit v1.2.3-70-g09d2


From 5425b61d62beef22d068e259ae3e2003f08e0c05 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 24 Oct 2022 15:06:47 -0400
Subject: switched drag element to show rows

---
 .../collectionSchema/CollectionSchemaView.scss     | 98 +++++++++++-----------
 .../collectionSchema/CollectionSchemaView.tsx      | 34 ++++----
 .../collections/collectionSchema/SchemaRowBox.tsx  | 14 ++--
 3 files changed, 73 insertions(+), 73 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 1d0ab459d..4d7e8c39f 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -7,22 +7,6 @@
     .schema-table {
         background-color: $white;
 
-        .schema-header-row,
-        .schema-row {
-            display: flex;
-            flex-direction: row;
-            max-height: 70px;
-            overflow: auto;
-
-            .schema-column-header,
-            .schema-table-cell,
-            .row-menu {
-                border: 1px solid $medium-gray;
-                padding: 5px;
-                overflow: hidden;
-            }
-        }
-
         .schema-header-row {
             justify-content: flex-end;
 
@@ -63,44 +47,60 @@
             }
         }
 
-        .schema-row {
-            justify-content: flex-end;
-
-            .row-menu {
-                display: flex;
-                flex-direction: row;
-                justify-content: center;
-                min-width: 50px;
-            }
-
-            .row-cells {
-                display: flex;
-                flex-direction: row;
-                justify-content: flex-end;
-            }
-        }
-
         .schema-header-menu {
             display: flex;
             flex-direction: row;
         }
+    }
+}
 
-        .row-button,
-        .schema-header-button {
-            width: 20px;
-            height: 20px;
-            border-radius: 100%;
-            background-color: $dark-gray;
-            color: white;
-            margin: 3px;
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            justify-content: center;
+.schema-header-row,
+.schema-row {
+    display: flex;
+    flex-direction: row;
+    max-height: 70px;
+    overflow: auto;
+
+    .schema-column-header,
+    .schema-table-cell,
+    .row-menu {
+        border: 1px solid $medium-gray;
+        padding: 5px;
+        overflow: hidden;
+    }
+}
 
-            svg {
-                width: 12px;
-            }
-        }
+.schema-row {
+    justify-content: flex-end;
+
+    .row-menu {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        min-width: 50px;
+    }
+
+    .row-cells {
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-end;
+    }
+}
+
+.schema-row-button,
+.schema-header-button {
+    width: 20px;
+    height: 20px;
+    border-radius: 100%;
+    background-color: $dark-gray;
+    color: white;
+    margin: 3px;
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    svg {
+        width: 12px;
     }
 }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 69a49598d..84a69d4b9 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,5 +1,5 @@
 import React = require('react');
-import { action, computed, observable, ObservableSet } from 'mobx';
+import { action, computed, observable, ObservableMap, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
@@ -41,7 +41,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _minColWidth: number = 120;
 
     @observable _rowMenuWidth: number = 100;
-    @observable _selectedDocs: ObservableSet = new ObservableSet<SchemaRowBox>();
+    @observable _selectedDocs: ObservableMap = new ObservableMap<SchemaRowBox, HTMLDivElement>();
     @observable _isDragging: boolean = false;
     @observable _displayColumnWidths: number[] | undefined;
 
@@ -149,7 +149,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    selectRow = (e: React.PointerEvent, doc: Doc, index: number) => {
+    selectRow = (e: React.PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
         const ctrl = e.ctrlKey || e.metaKey;
         const shift = e.shiftKey;
         if (shift && this._lastSelectedRow !== undefined) {
@@ -157,19 +157,19 @@ export class CollectionSchemaView extends CollectionSubView() {
             const endRow = Math.max(this._lastSelectedRow, index);
             for (let i = startRow; i <= endRow; i++) {
                 const currDoc: Doc = this.childDocs[i];
-                if (!this._selectedDocs.has(currDoc)) this._selectedDocs.add(currDoc);
+                if (!this._selectedDocs.has(currDoc)) this._selectedDocs.set(currDoc, ref);
             }
             this._lastSelectedRow = endRow;
         } else if (ctrl) {
             if (!this._selectedDocs.has(doc)) {
-                this._selectedDocs.add(doc);
+                this._selectedDocs.set(doc, ref);
                 this._lastSelectedRow = index;
             } else {
                 this._selectedDocs.delete(doc);
             }
         } else {
             this._selectedDocs.clear();
-            this._selectedDocs.add(doc);
+            this._selectedDocs.set(doc, ref);
             this._lastSelectedRow = index;
         }
 
@@ -219,25 +219,25 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    startDrag = (e: React.PointerEvent, doc: Doc) => {
+    startDrag = (e: React.PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
         if (!this._selectedDocs.has(doc)) {
             this._selectedDocs.clear();
-            this._selectedDocs.add(doc);
-            this._lastSelectedRow = this.childDocs.indexOf(doc);
+            this._selectedDocs.set(doc, ref);
+            this._lastSelectedRow = index;
             SelectionManager.SelectSchemaViewDoc(doc);
         }
         this._isDragging = true;
         this._selectedDocSortedArray = this.sortedSelectedDocs();
         const dragData = new DragManager.DocumentDragData(this._selectedDocSortedArray, 'move');
         dragData.moveDocument = this.props.moveDocument;
-        const dragItem: HTMLElement[] = [];
-        const dragDiv = document.createElement('div');
-        dragDiv.className = 'presItem-multiDrag';
-        dragDiv.innerText = 'Move ' + this._selectedDocs.size + '  row' + (this._selectedDocs.size > 1 ? 's' : '');
-        dragDiv.style.position = 'absolute';
-        dragDiv.style.top = e.clientY + 'px';
-        dragDiv.style.left = e.clientX - 50 + 'px';
-        dragItem.push(dragDiv);
+        const dragItem: HTMLElement[] = Array.from(this._selectedDocs.values());
+        // const dragDiv = document.createElement('div');
+        // dragDiv.className = 'presItem-multiDrag';
+        // dragDiv.innerText = 'Move ' + this._selectedDocs.size + '  row' + (this._selectedDocs.size > 1 ? 's' : '');
+        // dragDiv.style.position = 'absolute';
+        // dragDiv.style.top = e.clientY + 'px';
+        // dragDiv.style.left = e.clientX - 50 + 'px';
+        // dragItem.push(dragDiv);
 
         DragManager.StartDocumentDrag(
             dragItem.map(ele => ele),
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 661056553..66cc3a47a 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -17,9 +17,9 @@ export interface SchemaRowBoxProps extends FieldViewProps {
     columnKeys: string[];
     columnWidths: number[];
     rowMenuWidth: number;
-    selectedRows: ObservableSet<Doc>;
-    selectRow: (e: any, doc: Doc, index: number) => void;
-    startDrag: (e: any, doc: Doc) => boolean;
+    selectedRows: ObservableMap<Doc, HTMLDivElement>;
+    selectRow: (e: any, doc: Doc, ref: HTMLDivElement, index: number) => void;
+    startDrag: (e: any, doc: Doc, ref: HTMLDivElement, index: number) => boolean;
     dragging: boolean;
     dropIndex: (index: number) => void;
 }
@@ -42,9 +42,9 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
         setupMoveUpEvents(
             this,
             e,
-            e => this.props.startDrag(e, this.props.Document),
+            e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
             emptyFunction,
-            e => this.props.selectRow(e, this.props.Document, this.props.rowIndex)
+            e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
         );
     };
 
@@ -96,7 +96,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                 ref={(row: HTMLDivElement | null) => (this._ref = row)}>
                 <div className="row-menu" style={{ width: this.props.rowMenuWidth }}>
                     <div
-                        className="row-button"
+                        className="schema-row-button"
                         onPointerDown={undoBatch(e => {
                             e.stopPropagation();
                             this.props.removeDocument?.(this.props.Document);
@@ -104,7 +104,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                         <FontAwesomeIcon icon="times" />
                     </div>
                     <div
-                        className="row-button"
+                        className="schema-row-button"
                         onPointerDown={e => {
                             e.stopPropagation();
                             this.props.addDocTab(this.props.Document, 'add:right');
-- 
cgit v1.2.3-70-g09d2


From 213a92ba3aa39d144754029fde32b9d69b0f51cf Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 7 Nov 2022 12:57:44 -0500
Subject: basic key selection menu created

---
 .../collectionSchema/CollectionSchemaView.scss     |   8 ++
 .../collectionSchema/CollectionSchemaView.tsx      | 150 +++++++++++++++------
 .../collectionSchema/SchemaColumnHeader.tsx        | 105 +++++++++++++--
 .../collections/collectionSchema/SchemaRowBox.tsx  |  12 +-
 4 files changed, 223 insertions(+), 52 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 4d7e8c39f..0631cd21d 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -44,6 +44,13 @@
                 .schema-column-resizer.left {
                     align-self: flex-start;
                 }
+
+                .schema-column-menu {
+                    background: $light-gray;
+                    width: inherit;
+                    position: absolute;
+                    top: 35px;
+                }
             }
         }
 
@@ -72,6 +79,7 @@
 
 .schema-row {
     justify-content: flex-end;
+    background: white;
 
     .row-menu {
         display: flex;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 84a69d4b9..7516b95b8 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,7 +1,7 @@
 import React = require('react');
-import { action, computed, observable, ObservableMap, ObservableSet } from 'mobx';
+import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
@@ -41,10 +41,26 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _minColWidth: number = 120;
 
     @observable _rowMenuWidth: number = 100;
-    @observable _selectedDocs: ObservableMap = new ObservableMap<SchemaRowBox, HTMLDivElement>();
+    @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
+    @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
     @observable _isDragging: boolean = false;
     @observable _displayColumnWidths: number[] | undefined;
 
+    get documentKeys() {
+        const docs = this.childDocs;
+        const keys: { [key: string]: boolean } = {};
+        // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
+        //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
+        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
+        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
+        //  is displayed (unlikely) it won't show up until something else changes.
+        //TODO Types
+        untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
+
+        // this.columns.forEach(key => (keys[key.heading] = true));
+        return Array.from(Object.keys(keys));
+    }
+
     @computed get columnKeys() {
         return Cast(this.layoutDoc.columnKeys, listSpec('string'), defaultColumnKeys);
     }
@@ -64,6 +80,10 @@ export class CollectionSchemaView extends CollectionSubView() {
     @undoBatch
     @action
     changeColumnKey = (index: number, newKey: string) => {
+        if (!this.documentKeys.includes(newKey)) {
+            this.addNewKey(newKey);
+        }
+
         let currKeys = this.columnKeys;
         currKeys[index] = newKey;
         this.layoutDoc.columnKeys = new List<string>(currKeys);
@@ -72,10 +92,10 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @undoBatch
     @action
-    addColumn = (index: number) => {
-        let currKeys = this.columnKeys;
-        currKeys.splice(index, 0, 'title');
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+    addColumn = (index: number, key: string) => {
+        if (!this.documentKeys.includes(key)) {
+            this.addNewKey(key);
+        }
 
         const newColWidth = this._minColWidth;
         let currWidths = this.storedColumnWidths;
@@ -83,17 +103,22 @@ export class CollectionSchemaView extends CollectionSubView() {
             const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth);
             return proportion * (this.props.PanelWidth() - this._rowMenuWidth - newColWidth);
         });
-        currWidths.splice(index, 0, newColWidth);
+        currWidths.splice(index + 1, 0, newColWidth);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
+
+        let currKeys = this.columnKeys;
+        currKeys.splice(index + 1, 0, key);
+        this.layoutDoc.columnKeys = new List<string>(currKeys);
+    };
+
+    @action
+    addNewKey = (key: string) => {
+        this.childDocs.forEach(doc => (doc[key] = key + ' default val'));
     };
 
     @undoBatch
     @action
     removeColumn = (index: number) => {
-        let currKeys = this.columnKeys;
-        currKeys.splice(index, 1);
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
-
         let currWidths = this.storedColumnWidths;
         const removedColWidth = currWidths[index];
         currWidths = currWidths.map(w => {
@@ -102,6 +127,10 @@ export class CollectionSchemaView extends CollectionSubView() {
         });
         currWidths.splice(index, 1);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
+
+        let currKeys = this.columnKeys;
+        currKeys.splice(index, 1);
+        this.layoutDoc.columnKeys = new List<string>(currKeys);
     };
 
     @action
@@ -143,11 +172,55 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     finishResize = () => {
-        console.log('finished');
         this.layoutDoc.columnWidths = new List<number>(this._displayColumnWidths);
         this._displayColumnWidths = undefined;
     };
 
+    @undoBatch
+    @action
+    swapColumns = (index1: number, index2: number) => {
+        console.log(index1, index2);
+        const tempKey = this.columnKeys[index1];
+        const tempWidth = this.storedColumnWidths[index1];
+
+        let currKeys = this.columnKeys;
+        currKeys[index1] = currKeys[index2];
+        currKeys[index2] = tempKey;
+        this.layoutDoc.columnKeys = new List<string>(currKeys);
+
+        let currWidths = this.storedColumnWidths;
+        currWidths[index1] = currWidths[index2];
+        currWidths[index2] = tempWidth;
+        this.layoutDoc.columnWidths = new List<number>(currWidths);
+    };
+
+    @action
+    dragColumn = (e: any, index: number) => {
+        console.log(index);
+        e.stopPropagation();
+        e.preventDefault();
+        const rect = e.target.getBoundingClientRect();
+        if (e.clientX < rect.x) {
+            console.log('left', e.clientX, rect.x);
+            if (index < 1) return true;
+            this.swapColumns(index - 1, index);
+            return true;
+        }
+        if (e.clientX > rect.x + rect.width) {
+            console.log('right', e.clientX, rect.x + rect.width);
+            if (index === this.columnKeys.length) return true;
+            console.log(index);
+            this.swapColumns(index, index + 1);
+            return true;
+        }
+        return false;
+    };
+
+    @action
+    addRowRef = (doc: Doc, ref: HTMLDivElement) => {
+        this._rowEles.set(doc, ref);
+    };
+
     @action
     selectRow = (e: React.PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
         const ctrl = e.ctrlKey || e.metaKey;
@@ -157,19 +230,19 @@ export class CollectionSchemaView extends CollectionSubView() {
             const endRow = Math.max(this._lastSelectedRow, index);
             for (let i = startRow; i <= endRow; i++) {
                 const currDoc: Doc = this.childDocs[i];
-                if (!this._selectedDocs.has(currDoc)) this._selectedDocs.set(currDoc, ref);
+                if (!this._selectedDocs.has(currDoc)) this._selectedDocs.add(currDoc);
             }
             this._lastSelectedRow = endRow;
         } else if (ctrl) {
             if (!this._selectedDocs.has(doc)) {
-                this._selectedDocs.set(doc, ref);
+                this._selectedDocs.add(doc);
                 this._lastSelectedRow = index;
             } else {
                 this._selectedDocs.delete(doc);
             }
         } else {
             this._selectedDocs.clear();
-            this._selectedDocs.set(doc, ref);
+            this._selectedDocs.add(doc);
             this._lastSelectedRow = index;
         }
 
@@ -205,7 +278,6 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     onExternalDrop = async (e: React.DragEvent): Promise<void> => {
-        console.log('hello');
         super.onExternalDrop(
             e,
             {},
@@ -222,7 +294,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     startDrag = (e: React.PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
         if (!this._selectedDocs.has(doc)) {
             this._selectedDocs.clear();
-            this._selectedDocs.set(doc, ref);
+            this._selectedDocs.add(doc);
             this._lastSelectedRow = index;
             SelectionManager.SelectSchemaViewDoc(doc);
         }
@@ -230,14 +302,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._selectedDocSortedArray = this.sortedSelectedDocs();
         const dragData = new DragManager.DocumentDragData(this._selectedDocSortedArray, 'move');
         dragData.moveDocument = this.props.moveDocument;
-        const dragItem: HTMLElement[] = Array.from(this._selectedDocs.values());
-        // const dragDiv = document.createElement('div');
-        // dragDiv.className = 'presItem-multiDrag';
-        // dragDiv.innerText = 'Move ' + this._selectedDocs.size + '  row' + (this._selectedDocs.size > 1 ? 's' : '');
-        // dragDiv.style.position = 'absolute';
-        // dragDiv.style.top = e.clientY + 'px';
-        // dragDiv.style.left = e.clientX - 50 + 'px';
-        // dragItem.push(dragDiv);
+        const dragItem: HTMLElement[] = Array.from(this._selectedDocs.values()).map((doc: Doc) => this._rowEles.get(doc));
 
         DragManager.StartDocumentDrag(
             dragItem.map(ele => ele),
@@ -334,21 +399,27 @@ export class CollectionSchemaView extends CollectionSubView() {
                     this._ref = ele;
                     this.createDashEventsTarget(ele);
                 }}
-                onPointerDown={() => this._selectedDocs.clear()}
+                onPointerDown={action(() => {
+                    this._selectedDocs.clear();
+                })}
                 onDrop={this.onExternalDrop.bind(this)}>
                 <div className="schema-table">
                     <div className="schema-header-row">
-                        {this.columnKeys.map((key, index) => (
-                            <SchemaColumnHeader
-                                columnIndex={index}
-                                columnKeys={this.columnKeys}
-                                columnWidths={this.displayColumnWidths}
-                                changeColumnKey={this.changeColumnKey}
-                                addColumn={this.addColumn}
-                                removeColumn={this.removeColumn}
-                                resizeColumn={this.startResize}
-                            />
-                        ))}
+                        {this.columnKeys.map((key, index) => {
+                            return (
+                                <SchemaColumnHeader
+                                    columnIndex={index}
+                                    columnKeys={this.columnKeys}
+                                    columnWidths={this.displayColumnWidths}
+                                    possibleKeys={this.documentKeys}
+                                    changeColumnKey={this.changeColumnKey}
+                                    addColumn={this.addColumn}
+                                    removeColumn={this.removeColumn}
+                                    resizeColumn={this.startResize}
+                                    dragColumn={this.dragColumn}
+                                />
+                            );
+                        })}
                     </div>
                     <div className="schema-table-content">
                         {this.childDocs.map((doc: Doc, index: number) => (
@@ -361,11 +432,12 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 columnKeys={this.columnKeys}
                                 columnWidths={this.displayColumnWidths}
                                 rowMenuWidth={this._rowMenuWidth}
-                                selectedRows={this._selectedDocs}
+                                selectedDocs={this._selectedDocs}
                                 selectRow={this.selectRow}
                                 startDrag={this.startDrag}
                                 dragging={this._isDragging}
                                 dropIndex={this.setDropIndex}
+                                addRowRef={this.addRowRef}
                             />
                         ))}
                     </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index bee76bb24..a6140bafd 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,39 +1,124 @@
 import React = require('react');
-import { computed } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { EditableView } from '../../EditableView';
+import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import './CollectionSchemaView.scss';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 
 export interface SchemaColumnHeaderProps {
     columnKeys: string[];
     columnWidths: number[];
     columnIndex: number;
+    possibleKeys: string[];
     changeColumnKey: (index: number, newKey: string) => boolean;
-    addColumn: (index: number) => void;
+    addColumn: (index: number, key: string) => void;
     removeColumn: (index: number) => void;
     resizeColumn: (e: any, index: number, left: boolean) => void;
+    dragColumn: (e: any, index: number) => boolean;
 }
 
 @observer
 export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> {
+    @observable _menuVisible: boolean = false;
+    @observable _menuValue: string = '';
+    @observable _menuOptions: string[] = [];
+    private _makeNewColumn = false;
+
     @computed get fieldKey() {
         return this.props.columnKeys[this.props.columnIndex];
     }
 
+    @computed get renderColumnMenu() {
+        return (
+            <div className="schema-column-menu">
+                <input type="text" value={this._menuValue} onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
+                {this._menuOptions.map(key => (
+                    <div
+                        onPointerDown={e => {
+                            e.stopPropagation();
+                            this.setKey(key);
+                        }}>
+                        {key}
+                    </div>
+                ))}
+                <div
+                    onPointerDown={e => {
+                        e.stopPropagation();
+                        this.setKey(this._menuValue);
+                    }}>
+                    + new field
+                </div>
+            </div>
+        );
+    }
+
+    onSearchKeyDown = (e: React.KeyboardEvent) => {
+        switch (e.key) {
+            case 'Enter':
+                this.setKey(this._menuOptions.length > 0 ? this._menuOptions[0] : this._menuValue);
+                break;
+            case 'Escape':
+                this.toggleColumnMenu();
+                break;
+        }
+    };
+
+    @action
+    setKey = (key: string) => {
+        if (this._makeNewColumn) {
+            this.props.addColumn(this.props.columnIndex, key);
+        } else {
+            this.props.changeColumnKey(this.props.columnIndex, key);
+        }
+        this.toggleColumnMenu();
+    };
+
+    @action
+    updateKeySearch = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._menuValue = e.target.value;
+        this._menuOptions = this.props.possibleKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
+    };
+
+    @action
+    onPointerDown = (e: React.PointerEvent) => {
+        e.stopPropagation();
+
+        setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction);
+    };
+
+    @action
+    toggleColumnMenu = (newCol?: boolean) => {
+        this._makeNewColumn = false;
+        if (this._menuVisible) {
+            this._menuVisible = false;
+        } else {
+            this._menuVisible = true;
+            this._menuValue = this.fieldKey;
+            this._menuOptions = this.props.possibleKeys;
+            if (newCol) {
+                this._makeNewColumn = true;
+            }
+        }
+    };
+
     render() {
         return (
-            <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }}>
+            <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }} onPointerDown={this.onPointerDown}>
                 <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex, true)}></div>
-                <div className="schema-column-title">
-                    <EditableView SetValue={(newKey: string) => this.props.changeColumnKey(this.props.columnIndex, newKey)} GetValue={() => this.fieldKey} contents={this.fieldKey} />
-                </div>
+                <div className="schema-column-title">{this.fieldKey}</div>
 
                 <div className="schema-header-menu">
                     <div
                         className="schema-header-button"
                         onPointerDown={e => {
-                            this.props.addColumn(this.props.columnIndex + 1);
+                            this.toggleColumnMenu();
+                        }}>
+                        <FontAwesomeIcon icon="pencil-alt" />
+                    </div>
+                    <div
+                        className="schema-header-button"
+                        onPointerDown={e => {
+                            this.toggleColumnMenu(true);
                         }}>
                         <FontAwesomeIcon icon="plus" />
                     </div>
@@ -47,6 +132,8 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
                 </div>
 
                 <div className="schema-column-resizer right" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex, false)}></div>
+
+                {this._menuVisible && this.renderColumnMenu}
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 66cc3a47a..bfce61952 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -17,11 +17,12 @@ export interface SchemaRowBoxProps extends FieldViewProps {
     columnKeys: string[];
     columnWidths: number[];
     rowMenuWidth: number;
-    selectedRows: ObservableMap<Doc, HTMLDivElement>;
+    selectedDocs: ObservableSet<Doc>;
     selectRow: (e: any, doc: Doc, ref: HTMLDivElement, index: number) => void;
     startDrag: (e: any, doc: Doc, ref: HTMLDivElement, index: number) => boolean;
     dragging: boolean;
     dropIndex: (index: number) => void;
+    addRowRef: (doc: Doc, ref: HTMLDivElement) => void;
 }
 
 @observer
@@ -32,7 +33,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
 
     private _ref: HTMLDivElement | null = null;
 
-    isSelected = () => this.props.selectedRows.has(this.props.Document);
+    isSelected = () => this.props.selectedDocs.has(this.props.Document);
     bounds = () => this._ref?.getBoundingClientRect();
 
     @action
@@ -58,7 +59,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
         if (!this.props.dragging) return;
         let dragIsRow: boolean = true;
         DragManager.docsBeingDragged.forEach(doc => {
-            dragIsRow = this.props.selectedRows.has(doc);
+            dragIsRow = this.props.selectedDocs.has(doc);
         });
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
@@ -93,7 +94,10 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                 onPointerDown={this.onRowPointerDown}
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
-                ref={(row: HTMLDivElement | null) => (this._ref = row)}>
+                ref={(row: HTMLDivElement | null) => {
+                    row && this.props.addRowRef(this.props.Document, row);
+                    this._ref = row;
+                }}>
                 <div className="row-menu" style={{ width: this.props.rowMenuWidth }}>
                     <div
                         className="schema-row-button"
-- 
cgit v1.2.3-70-g09d2


From 73d3c63658c4bdf3268ea81a02eb96566869b855 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 21 Nov 2022 12:38:37 -0500
Subject: column deletion and new key menu fixes

---
 .../collectionSchema/CollectionSchemaView.scss     | 13 +++-
 .../collectionSchema/CollectionSchemaView.tsx      | 73 +++++++++++++---------
 .../collectionSchema/SchemaColumnHeader.tsx        | 31 +++++----
 .../collections/collectionSchema/SchemaRowBox.tsx  |  2 +-
 4 files changed, 74 insertions(+), 45 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 99f49eb0e..4fa5d80e2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -78,6 +78,15 @@
                         align-items: flex-start;
                     }
 
+                    .schema-new-key-options {
+                        margin: 10px;
+                        .schema-key-warning {
+                            color: red;
+                            font-weight: normal;
+                            align-self: center;
+                        }
+                    }
+
                     .schema-key-list {
                         width: 100%;
                         max-height: 300px;
@@ -85,7 +94,7 @@
                     }
 
                     .schema-key-type-option {
-                        margin: 2px 10px;
+                        margin: 2px 0px;
 
                         input {
                             margin-right: 5px;
@@ -93,7 +102,7 @@
                     }
 
                     .schema-key-default-val {
-                        margin: 5px 10px;
+                        margin: 5px 0;
                     }
 
                     .schema-column-menu-button {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 24008a21d..f7c68c803 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,5 +1,5 @@
 import React = require('react');
-import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx';
+import { action, computed, observable, ObservableMap, ObservableSet, trace, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
@@ -66,11 +66,22 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get storedColumnWidths() {
-        return Cast(
+        let widths = Cast(
             this.layoutDoc.columnWidths,
             listSpec('number'),
             this.columnKeys.map(() => (this.props.PanelWidth() - this._rowMenuWidth) / this.columnKeys.length)
         );
+
+        const totalWidth = widths.reduce((sum, width) => sum + width, 0);
+        if (totalWidth !== this.props.PanelWidth() - this._rowMenuWidth) {
+            widths = widths.map(w => {
+                const proportion = w / totalWidth;
+                return proportion * (this.props.PanelWidth() - this._rowMenuWidth);
+            });
+            // this.layoutDoc.columnWidths = new List<number>(widths);
+        }
+
+        return widths;
     }
 
     @computed get displayColumnWidths() {
@@ -84,7 +95,7 @@ export class CollectionSchemaView extends CollectionSubView() {
             this.addNewKey(newKey, defaultVal);
         }
 
-        let currKeys = this.columnKeys;
+        let currKeys = [...this.columnKeys];
         currKeys[index] = newKey;
         this.layoutDoc.columnKeys = new List<string>(currKeys);
     };
@@ -96,7 +107,7 @@ export class CollectionSchemaView extends CollectionSubView() {
             this.addNewKey(key, defaultVal);
         }
 
-        let currWidths = this.storedColumnWidths;
+        let currWidths = [...this.storedColumnWidths];
         const newColWidth = this.props.PanelWidth() / (currWidths.length + 1);
         currWidths = currWidths.map(w => {
             const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth);
@@ -105,21 +116,21 @@ export class CollectionSchemaView extends CollectionSubView() {
         currWidths.splice(index + 1, 0, newColWidth);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
 
-        let currKeys = this.columnKeys;
+        let currKeys = [...this.columnKeys];
         currKeys.splice(index + 1, 0, key);
         this.layoutDoc.columnKeys = new List<string>(currKeys);
     };
 
     @action
     addNewKey = (key: string, defaultVal: any) => {
-        console.log(defaultVal);
         this.childDocs.forEach(doc => (doc[key] = defaultVal));
     };
 
     @undoBatch
     @action
     removeColumn = (index: number) => {
-        let currWidths = this.storedColumnWidths;
+        if (this.columnKeys.length === 1) return;
+        let currWidths = [...this.storedColumnWidths];
         const removedColWidth = currWidths[index];
         currWidths = currWidths.map(w => {
             const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth - removedColWidth);
@@ -128,9 +139,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         currWidths.splice(index, 1);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
 
-        let currKeys = this.columnKeys;
+        let currKeys = [...this.columnKeys];
         currKeys.splice(index, 1);
         this.layoutDoc.columnKeys = new List<string>(currKeys);
+        console.log([...this.storedColumnWidths]);
+        console.log([...this.columnKeys]);
     };
 
     @action
@@ -179,7 +192,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     @undoBatch
     @action
     swapColumns = (index1: number, index2: number) => {
-        console.log(index1, index2);
         const tempKey = this.columnKeys[index1];
         const tempWidth = this.storedColumnWidths[index1];
 
@@ -194,27 +206,23 @@ export class CollectionSchemaView extends CollectionSubView() {
         this.layoutDoc.columnWidths = new List<number>(currWidths);
     };
 
-    @action
-    dragColumn = (e: any, index: number) => {
-        console.log(index);
-        e.stopPropagation();
-        e.preventDefault();
-        const rect = e.target.getBoundingClientRect();
-        if (e.clientX < rect.x) {
-            console.log('left', e.clientX, rect.x);
-            if (index < 1) return true;
-            this.swapColumns(index - 1, index);
-            return true;
-        }
-        if (e.clientX > rect.x + rect.width) {
-            console.log('right', e.clientX, rect.x + rect.width);
-            if (index === this.columnKeys.length) return true;
-            console.log(index);
-            this.swapColumns(index, index + 1);
-            return true;
-        }
-        return false;
-    };
+    // @action
+    // dragColumn = (e: any, index: number) => {
+    //     e.stopPropagation();
+    //     e.preventDefault();
+    //     const rect = e.target.getBoundingClientRect();
+    //     if (e.clientX < rect.x) {
+    //         if (index < 1) return true;
+    //         this.swapColumns(index - 1, index);
+    //         return true;
+    //     }
+    //     if (e.clientX > rect.x + rect.width) {
+    //         if (index === this.columnKeys.length) return true;
+    //         this.swapColumns(index, index + 1);
+    //         return true;
+    //     }
+    //     return false;
+    // };
 
     @action
     addRowRef = (doc: Doc, ref: HTMLDivElement) => {
@@ -405,9 +413,11 @@ export class CollectionSchemaView extends CollectionSubView() {
                 onDrop={this.onExternalDrop.bind(this)}>
                 <div className="schema-table">
                     <div className="schema-header-row">
+                        <div className="row-menu" style={{ width: this._rowMenuWidth }}></div>
                         {this.columnKeys.map((key, index) => {
                             return (
                                 <SchemaColumnHeader
+                                    key={index}
                                     columnIndex={index}
                                     columnKeys={this.columnKeys}
                                     columnWidths={this.displayColumnWidths}
@@ -416,7 +426,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                     addColumn={this.addColumn}
                                     removeColumn={this.removeColumn}
                                     resizeColumn={this.startResize}
-                                    dragColumn={this.dragColumn}
+                                    // dragColumn={this.dragColumn}
                                 />
                             );
                         })}
@@ -425,6 +435,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                         {this.childDocs.map((doc: Doc, index: number) => (
                             <SchemaRowBox
                                 {...this.props}
+                                key={index}
                                 Document={doc}
                                 ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                                 ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index b9e25a473..a6a5f66ab 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -15,7 +15,7 @@ export interface SchemaColumnHeaderProps {
     addColumn: (index: number, key: string, defaultVal?: any) => void;
     removeColumn: (index: number) => void;
     resizeColumn: (e: any, index: number, left: boolean) => void;
-    dragColumn: (e: any, index: number) => boolean;
+    // dragColumn: (e: any, index: number) => boolean;
 }
 
 @observer
@@ -24,6 +24,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
     @observable _makeNewField: boolean = false;
     @observable _newFieldType: ColumnType = ColumnType.Number;
     @observable _newFieldDefault: any = 0;
+    @observable _newFieldWarning: string = '';
     @observable _menuValue: string = '';
     @observable _menuOptions: string[] = [];
     private _makeNewColumn = false;
@@ -94,11 +95,17 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
                             string
                         </div>
                         <div className="schema-key-default-val">value: {this.fieldDefaultInput}</div>
+                        <div className="schema-key-warning">{this._newFieldWarning}</div>
                         <div
                             className="schema-column-menu-button"
                             onPointerDown={action(e => {
-                                this.setKey(this._menuValue, this._newFieldDefault);
-                                this._makeNewField = false;
+                                if (this.props.possibleKeys.includes(this._menuValue)) {
+                                    this._newFieldWarning = 'Field already exists';
+                                } else if (this._menuValue.length === 0) {
+                                    this._newFieldWarning = 'Field cannot be an empty string';
+                                } else {
+                                    this.setKey(this._menuValue, this._newFieldDefault);
+                                }
                             })}>
                             done
                         </div>
@@ -134,7 +141,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
     onSearchKeyDown = (e: React.KeyboardEvent) => {
         switch (e.key) {
             case 'Enter':
-                this.setKey(this._menuOptions.length > 0 ? this._menuOptions[0] : this._menuValue);
+                this.setKey(this._menuOptions.length > 0 && this._menuValue.length > 0 ? this._menuOptions[0] : this._menuValue);
                 break;
             case 'Escape':
                 this.toggleColumnMenu();
@@ -158,12 +165,12 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
         this._menuOptions = this.props.possibleKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
     };
 
-    @action
-    onPointerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
+    // @action
+    // onPointerDown = (e: React.PointerEvent) => {
+    //     e.stopPropagation();
 
-        setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction);
-    };
+    //     setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction);
+    // };
 
     @action
     toggleColumnMenu = (newCol?: boolean) => {
@@ -172,9 +179,11 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
             this._menuVisible = false;
         } else {
             this._menuVisible = true;
-            this._menuValue = this.fieldKey;
+            this._menuValue = '';
             this._menuOptions = this.props.possibleKeys;
             this._makeNewField = false;
+            this._newFieldWarning = '';
+            this._makeNewField = false;
             if (newCol) {
                 this._makeNewColumn = true;
             }
@@ -183,7 +192,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
 
     render() {
         return (
-            <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }} onPointerDown={this.onPointerDown}>
+            <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }}>
                 <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex, true)}></div>
                 <div className="schema-column-title">{this.fieldKey}</div>
 
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index bfce61952..5c1f32565 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -118,7 +118,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                 </div>
                 <div className="row-cells">
                     {this.props.columnKeys.map((key, index) => (
-                        <SchemaTableCell Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
                     ))}
                 </div>
             </div>
-- 
cgit v1.2.3-70-g09d2


From 9d2af1180f0dd5af5ab86b922cd8b0cdfcf4ea09 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Sat, 28 Jan 2023 22:38:41 -0500
Subject: checkpoint: schemarow as documentview functioning

---
 package-lock.json                                  |   39 -
 src/client/documents/Documents.ts                  |    4 +
 .../collectionSchema/CollectionSchemaView.scss     |    6 +
 .../collectionSchema/CollectionSchemaView.tsx      |  129 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   80 +-
 .../OldCollectionSchemaCells.tsx                   | 1366 +++++++++----------
 .../OldCollectionSchemaMovableColumn.tsx           |  250 ++--
 .../OldCollectionSchemaMovableRow.tsx              |  274 ++--
 .../OldCollectionSchemaView.tsx                    | 1298 +++++++++---------
 .../old_collectionSchema/OldSchemaTable.tsx        | 1388 ++++++++++----------
 src/client/views/nodes/DocumentContentsView.tsx    |    2 +
 11 files changed, 2451 insertions(+), 2385 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 26e7a41d7..7f93ef3c8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5303,16 +5303,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6480,28 +6470,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.62",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
-            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6513,7 +6481,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -21699,12 +21666,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1fd07d61d..58e318879 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1150,6 +1150,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options });
         }
 
+        export function SchemaRowDocument(options?: DocumentOptions) {
+            return InstanceFromProto(Prototypes.get(DocumentType.SCHEMAROW), undefined, { ...(options || {}) });
+        }
+
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, viewType: CollectionViewType.Docking, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 4fa5d80e2..e0d0101a2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -126,10 +126,16 @@
     }
 }
 
+.schema-row-wrapper {
+    max-height: 70px;
+    overflow: hidden;
+}
+
 .schema-header-row,
 .schema-row {
     display: flex;
     flex-direction: row;
+    height: 100%;
     max-height: 70px;
     overflow: auto;
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f7c68c803..c9f934aec 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,14 +1,14 @@
 import React = require('react');
 import { action, computed, observable, ObservableMap, ObservableSet, trace, untracked } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
+import { DataSym, Doc, DocListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
-import { Cast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -21,6 +21,9 @@ import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
+import { DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { Transform } from '../../../util/Transform';
 
 export enum ColumnType {
     Number,
@@ -40,7 +43,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _closestDropIndex: number = 0;
     private _minColWidth: number = 150;
 
-    @observable _rowMenuWidth: number = 100;
+    public static _rowMenuWidth: number = 100;
     @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
     @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
     @observable _isDragging: boolean = false;
@@ -69,16 +72,15 @@ export class CollectionSchemaView extends CollectionSubView() {
         let widths = Cast(
             this.layoutDoc.columnWidths,
             listSpec('number'),
-            this.columnKeys.map(() => (this.props.PanelWidth() - this._rowMenuWidth) / this.columnKeys.length)
+            this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
         );
 
         const totalWidth = widths.reduce((sum, width) => sum + width, 0);
-        if (totalWidth !== this.props.PanelWidth() - this._rowMenuWidth) {
+        if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) {
             widths = widths.map(w => {
                 const proportion = w / totalWidth;
-                return proportion * (this.props.PanelWidth() - this._rowMenuWidth);
+                return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
             });
-            // this.layoutDoc.columnWidths = new List<number>(widths);
         }
 
         return widths;
@@ -110,8 +112,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         let currWidths = [...this.storedColumnWidths];
         const newColWidth = this.props.PanelWidth() / (currWidths.length + 1);
         currWidths = currWidths.map(w => {
-            const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth);
-            return proportion * (this.props.PanelWidth() - this._rowMenuWidth - newColWidth);
+            const proportion = w / (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
+            return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth - newColWidth);
         });
         currWidths.splice(index + 1, 0, newColWidth);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
@@ -133,8 +135,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         let currWidths = [...this.storedColumnWidths];
         const removedColWidth = currWidths[index];
         currWidths = currWidths.map(w => {
-            const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth - removedColWidth);
-            return proportion * (this.props.PanelWidth() - this._rowMenuWidth);
+            const proportion = w / (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth - removedColWidth);
+            return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
         });
         currWidths.splice(index, 1);
         this.layoutDoc.columnWidths = new List<number>(currWidths);
@@ -142,8 +144,6 @@ export class CollectionSchemaView extends CollectionSubView() {
         let currKeys = [...this.columnKeys];
         currKeys.splice(index, 1);
         this.layoutDoc.columnKeys = new List<string>(currKeys);
-        console.log([...this.storedColumnWidths]);
-        console.log([...this.columnKeys]);
     };
 
     @action
@@ -399,6 +399,34 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
+    focusDocument = (doc: Doc, options: DocFocusOptions) => {
+        Doc.BrushDoc(doc);
+
+        let focusSpeed = 0;
+        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
+        if (found) {
+            const top = found.getBoundingClientRect().top;
+            const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
+            if (Math.floor(localTop[1]) !== 0) {
+                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
+            }
+        }
+        const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
+        this.props.focus(this.rootDoc, {
+            ...options,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
+        });
+    };
+
+    isChildContentActive = () =>
+        this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
+
+    getDocTransform(doc: Doc, dref?: DocumentView) {
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
+        // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
+        return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
+    }
+
     render() {
         return (
             <div
@@ -413,7 +441,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 onDrop={this.onExternalDrop.bind(this)}>
                 <div className="schema-table">
                     <div className="schema-header-row">
-                        <div className="row-menu" style={{ width: this._rowMenuWidth }}></div>
+                        <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}></div>
                         {this.columnKeys.map((key, index) => {
                             return (
                                 <SchemaColumnHeader
@@ -432,25 +460,58 @@ export class CollectionSchemaView extends CollectionSubView() {
                         })}
                     </div>
                     <div className="schema-table-content">
-                        {this.childDocs.map((doc: Doc, index: number) => (
-                            <SchemaRowBox
-                                {...this.props}
-                                key={index}
-                                Document={doc}
-                                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                                ContainingCollectionView={this.props.CollectionView}
-                                rowIndex={index}
-                                columnKeys={this.columnKeys}
-                                columnWidths={this.displayColumnWidths}
-                                rowMenuWidth={this._rowMenuWidth}
-                                selectedDocs={this._selectedDocs}
-                                selectRow={this.selectRow}
-                                startDrag={this.startDrag}
-                                dragging={this._isDragging}
-                                dropIndex={this.setDropIndex}
-                                addRowRef={this.addRowRef}
-                            />
-                        ))}
+                        {this.childDocs.map((doc: Doc, index: number) => {
+                            const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
+                            let dref: Opt<DocumentView>;
+
+                            return (
+                                <div className="schema-row-wrapper">
+                                    <DocumentView
+                                        {...this.props}
+                                        ref={r => (dref = r || undefined)}
+                                        LayoutTemplate={this.props.childLayoutTemplate}
+                                        LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
+                                        renderDepth={this.props.renderDepth + 1}
+                                        Document={doc}
+                                        DataDoc={dataDoc}
+                                        ContainingCollectionView={this.props.CollectionView}
+                                        ContainingCollectionDoc={this.Document}
+                                        PanelWidth={this.props.PanelWidth}
+                                        PanelHeight={() => 70}
+                                        styleProvider={DefaultStyleProvider}
+                                        focus={this.focusDocument}
+                                        docFilters={this.childDocFilters}
+                                        docRangeFilters={this.childDocRangeFilters}
+                                        searchFilterDocs={this.searchFilterDocs}
+                                        rootSelected={this.rootSelected}
+                                        ScreenToLocalTransform={() => this.getDocTransform(doc, dref)}
+                                        bringToFront={emptyFunction}
+                                        isContentActive={this.isChildContentActive}
+                                        hideDecorations={true}
+                                        hideTitle={true}
+                                        hideDocumentButtonBar={true}
+                                    />
+                                </div>
+                            );
+
+                            // <SchemaRowBox
+                            //     {...this.props}
+                            //     key={index}
+                            //     Document={doc}
+                            //     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                            //     ContainingCollectionView={this.props.CollectionView}
+                            //     rowIndex={index}
+                            //     columnKeys={this.columnKeys}
+                            //     columnWidths={this.displayColumnWidths}
+                            //     rowMenuWidth={CollectionSchemaView._rowMenuWidth}
+                            //     selectedDocs={this._selectedDocs}
+                            //     selectRow={this.selectRow}
+                            //     startDrag={this.startDrag}
+                            //     dragging={this._isDragging}
+                            //     dropIndex={this.setDropIndex}
+                            //     addRowRef={this.addRowRef}
+                            // />
+                        })}
                     </div>
                 </div>
                 <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 5c1f32565..f790e9dbf 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,8 +1,8 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, ObservableMap, ObservableSet } from 'mobx';
+import { action, computed, ObservableMap, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc } from '../../../../fields/Doc';
+import { Doc, StrListCast } from '../../../../fields/Doc';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
@@ -11,6 +11,10 @@ import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
 import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
+import { OpenWhere } from '../../nodes/DocumentView';
+import { Cast } from '../../../../fields/Types';
+import { listSpec } from '../../../../fields/Schema';
+import { CollectionSchemaView } from './CollectionSchemaView';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     rowIndex: number;
@@ -26,40 +30,61 @@ export interface SchemaRowBoxProps extends FieldViewProps {
 }
 
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(SchemaRowBox, fieldKey);
     }
 
     private _ref: HTMLDivElement | null = null;
 
-    isSelected = () => this.props.selectedDocs.has(this.props.Document);
     bounds = () => this._ref?.getBoundingClientRect();
 
+    @computed get columnKeys() {
+        return StrListCast(this.props.ContainingCollectionDoc?.columnKeys);
+    }
+
+    @computed get storedColumnWidths() {
+        let widths = Cast(
+            this.props.ContainingCollectionDoc?.columnWidths,
+            listSpec('number'),
+            this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
+        );
+
+        const totalWidth = widths.reduce((sum, width) => sum + width, 0);
+        if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) {
+            widths = widths.map(w => {
+                const proportion = w / totalWidth;
+                return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
+            });
+        }
+
+        return widths;
+    }
+
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
 
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
-            emptyFunction,
-            e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
-        );
+        // setupMoveUpEvents(
+        //     this,
+        //     e,
+        //     e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
+        //     emptyFunction,
+        //     e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
+        // );
     };
 
     onPointerEnter = (e: any) => {
-        if (!this.props.dragging) return;
+        // if (!this.props.dragging) return;
         document.removeEventListener('pointermove', this.onPointerMove);
         document.addEventListener('pointermove', this.onPointerMove);
     };
 
     onPointerMove = (e: any) => {
-        if (!this.props.dragging) return;
+        // if (!this.props.dragging) return;
         let dragIsRow: boolean = true;
         DragManager.docsBeingDragged.forEach(doc => {
-            dragIsRow = this.props.selectedDocs.has(doc);
+            // dragIsRow = this.props.selectedDocs.has(doc);
         });
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
@@ -69,11 +94,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
             if (y <= halfLine) {
                 this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
                 this._ref.style.borderBottom = '0px';
-                this.props.dropIndex(this.props.rowIndex);
+                // this.props.dropIndex(this.props.rowIndex);
             } else if (y > halfLine) {
                 this._ref.style.borderTop = '0px';
                 this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                this.props.dropIndex(this.props.rowIndex + 1);
+                // this.props.dropIndex(this.props.rowIndex + 1);
             }
         }
     };
@@ -90,15 +115,22 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
         return (
             <div
                 className="schema-row"
-                style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
-                onPointerDown={this.onRowPointerDown}
-                onPointerEnter={this.onPointerEnter}
+                style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE } : {}}
+                // style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
+                // onPointerDown={this.onRowPointerDown}
+                // onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    row && this.props.addRowRef(this.props.Document, row);
+                    // row && this.props.addRowRef(this.props.Document, row);
                     this._ref = row;
                 }}>
-                <div className="row-menu" style={{ width: this.props.rowMenuWidth }}>
+                <div
+                    className="row-menu"
+                    style={
+                        {
+                            /* width: this.props.rowMenuWidth */
+                        }
+                    }>
                     <div
                         className="schema-row-button"
                         onPointerDown={undoBatch(e => {
@@ -111,14 +143,14 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                         className="schema-row-button"
                         onPointerDown={e => {
                             e.stopPropagation();
-                            this.props.addDocTab(this.props.Document, 'add:right');
+                            this.props.addDocTab(this.props.Document, OpenWhere.addRight);
                         }}>
                         <FontAwesomeIcon icon="external-link-alt" />
                     </div>
                 </div>
                 <div className="row-cells">
-                    {this.props.columnKeys.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+                    {this.columnKeys.map((key, index) => (
+                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.storedColumnWidths[index]} />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
index 97a6c5c18..5953f85ad 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
@@ -1,683 +1,683 @@
-import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import { extname } from 'path';
-import DatePicker from 'react-datepicker';
-import { CellInfo } from 'react-table';
-import { DateField } from '../../../../fields/DateField';
-import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { List } from '../../../../fields/List';
-import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { ComputedField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DateCast, FieldValue, StrCast } from '../../../../fields/Types';
-import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, Utils } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager } from '../../../util/DragManager';
-import { KeyCodes } from '../../../util/KeyCodes';
-import { CompileScript } from '../../../util/Scripting';
-import { SearchUtil } from '../../../util/SearchUtil';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { undoBatch } from '../../../util/UndoManager';
-import '../../../views/DocumentDecorations.scss';
-import { EditableView } from '../../EditableView';
-import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
-import { DocumentIconContainer } from '../../nodes/DocumentIcon';
-import { OverlayView } from '../../OverlayView';
-import { CollectionView } from '../CollectionView';
-import './CollectionSchemaView.scss';
-import { OpenWhere } from '../../nodes/DocumentView';
-import { PinProps } from '../../nodes/trails';
-
-// intialize cell properties
-export interface CellProps {
-    row: number;
-    col: number;
-    rowProps: CellInfo;
-    // currently unused
-    CollectionView: Opt<CollectionView>;
-    // currently unused
-    ContainingCollection: Opt<CollectionView>;
-    Document: Doc;
-    // column name
-    fieldKey: string;
-    // currently unused
-    renderDepth: number;
-    // called when a button is pressed on the node itself
-    addDocTab: (document: Doc, where: OpenWhere) => boolean;
-    pinToPres: (document: Doc, pinProps: PinProps) => void;
-    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    isFocused: boolean;
-    changeFocusedCellByIndex: (row: number, col: number) => void;
-    // set whether the cell is in the isEditing mode
-    setIsEditing: (isEditing: boolean) => void;
-    isEditable: boolean;
-    setPreviewDoc: (doc: Doc) => void;
-    setComputed: (script: string, doc: Doc, field: string, row: number, col: number) => boolean;
-    getField: (row: number, col?: number) => void;
-    // currnetly unused
-    showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void;
-}
-
-@observer
-export class CollectionSchemaCell extends React.Component<CellProps> {
-    // return a field key that is corrected for whether it COMMENT
-    public static resolvedFieldKey(column: string, rowDoc: Doc) {
-        const fieldKey = column;
-        if (fieldKey.startsWith('*')) {
-            const rootKey = fieldKey.substring(1);
-            const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))];
-            const matchedKeys = allKeys.filter(key => key.includes(rootKey));
-            if (matchedKeys.length) return matchedKeys[0];
-        }
-        return fieldKey;
-    }
-    @observable protected _isEditing: boolean = false;
-    protected _focusRef = React.createRef<HTMLDivElement>();
-    protected _rowDoc = this.props.rowProps.original;
-    // Gets the serialized data in proto form of the base proto that this document's proto inherits from
-    protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original);
-    // methods for dragging and dropping
-    protected _dropDisposer?: DragManager.DragDropDisposer;
-    @observable contents: string = '';
-
-    componentDidMount() {
-        document.addEventListener('keydown', this.onKeyDown);
-    }
-    componentWillUnmount() {
-        document.removeEventListener('keydown', this.onKeyDown);
-    }
-
-    @action
-    onKeyDown = (e: KeyboardEvent): void => {
-        // If a cell is editable and clicked, hitting enter shoudl allow the user to edit it
-        if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) {
-            document.removeEventListener('keydown', this.onKeyDown);
-            this._isEditing = true;
-            this.props.setIsEditing(true);
-        }
-    };
-
-    @action
-    isEditingCallback = (isEditing: boolean): void => {
-        // a general method that takes a boolean that determines whether the cell should be in
-        // is-editing mode
-        // remove the event listener if it's there
-        document.removeEventListener('keydown', this.onKeyDown);
-        // it's not already in is-editing mode, re-add the event listener
-        isEditing && document.addEventListener('keydown', this.onKeyDown);
-        this._isEditing = isEditing;
-        this.props.setIsEditing(isEditing);
-        this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
-    };
-
-    @action
-    onPointerDown = async (e: React.PointerEvent): Promise<void> => {
-        // pan to the cell
-        this.onItemDown(e);
-        // focus on it
-        this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
-        this.props.setPreviewDoc(this.props.rowProps.original);
-
-        let url: string;
-        if ((url = StrCast(this.props.rowProps.row.href))) {
-            // opens up the the doc in a new window, blurring the old one
-            try {
-                new URL(url);
-                const temp = window.open(url)!;
-                temp.blur();
-                window.focus();
-            } catch {}
-        }
-
-        const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null);
-        doc && this.props.setPreviewDoc(doc);
-    };
-
-    @undoBatch
-    applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => {
-        // apply a specified change to the cell
-        const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) });
-        if (!res.success) return false;
-        // change what is rendered to this new changed cell content
-        doc[this.renderFieldKey] = res.result;
-        return true;
-        // return whether the change was successful
-    };
-
-    private drop = (e: Event, de: DragManager.DropEvent) => {
-        // if the drag has data at its completion
-        if (de.complete.docDragData) {
-            // if only one doc was dragged
-            if (de.complete.docDragData.draggedDocuments.length === 1) {
-                // update the renderFieldKey
-                this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
-            } else {
-                // create schema document reflecting the new column arrangement
-                const coll = Docs.Create.SchemaDocument([new SchemaHeaderField('title', '#f1efeb')], de.complete.docDragData.draggedDocuments, {});
-                this._rowDataDoc[this.renderFieldKey] = coll;
-            }
-            e.stopPropagation();
-        }
-    };
-
-    protected dropRef = (ele: HTMLElement | null) => {
-        // if the drop disposer is not undefined, run its function
-        this._dropDisposer?.();
-        // if ele is not null, give ele a non-undefined drop disposer
-        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
-    };
-
-    returnHighlights(contents: string, positions?: number[]) {
-        if (positions) {
-            const results = [];
-            StrCast(this.props.Document._searchString);
-            const length = StrCast(this.props.Document._searchString).length;
-            const color = contents ? 'black' : 'grey';
-
-            results.push(
-                <span key="-1" style={{ color }}>
-                    {contents?.slice(0, positions[0])}
-                </span>
-            );
-            positions.forEach((num, cur) => {
-                results.push(
-                    <span key={'start' + cur} style={{ backgroundColor: '#FFFF00', color }}>
-                        {contents?.slice(num, num + length)}
-                    </span>
-                );
-                let end = 0;
-                cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]);
-                results.push(
-                    <span key={'end' + cur} style={{ color }}>
-                        {contents?.slice(num + length, end)}
-                    </span>
-                );
-            });
-            return results;
-        }
-        return <span style={{ color: contents ? 'black' : 'grey' }}>{contents ? contents?.valueOf() : 'undefined'}</span>;
-    }
-
-    @computed get renderFieldKey() {
-        // gets the resolved field key of this cell
-        return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original);
-    }
-
-    onItemDown = async (e: React.PointerEvent) => {
-        // if the document is a document used to change UI for search results in schema view
-        if (this.props.Document._searchDoc) {
-            const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
-            const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
-            // Jump to the this document
-            DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc));
-        }
-    };
-
-    renderCellWithType(type: string | undefined) {
-        const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-
-        // the column
-        const fieldKey = this.renderFieldKey;
-        // the exact cell
-        const field = this._rowDoc[fieldKey];
-
-        const onPointerEnter = (e: React.PointerEvent): void => {
-            // e.buttons === 1 means the left moue pointer is down
-            if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === 'document' || type === undefined)) {
-                dragRef.current!.className = 'collectionSchemaView-cellContainer doc-drag-over';
-            }
-        };
-        const onPointerLeave = (e: React.PointerEvent): void => {
-            // change the class name to indicate that the cell is no longer being dragged
-            dragRef.current!.className = 'collectionSchemaView-cellContainer';
-        };
-
-        let contents = Field.toString(field as Field);
-        // display 2 hyphens instead of a blank box for empty cells
-        contents = contents === '' ? '--' : contents;
-
-        // classname reflects the tatus of the cell
-        let className = 'collectionSchemaView-cellWrapper';
-        if (this._isEditing) className += ' editing';
-        if (this.props.isFocused && this.props.isEditable) className += ' focused';
-        if (this.props.isFocused && !this.props.isEditable) className += ' inactive';
-
-        const positions = [];
-        if (StrCast(this.props.Document._searchString).toLowerCase() !== '') {
-            // term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents
-            let term = field instanceof Promise ? '...promise pending...' : contents.toLowerCase();
-            const search = StrCast(this.props.Document._searchString).toLowerCase();
-            let start = term.indexOf(search);
-            let tally = 0;
-            // if search is found in term
-            if (start !== -1) {
-                positions.push(start);
-            }
-            // if search is found in term, continue finding all instances of search in term
-            while (start < contents?.length && start !== -1) {
-                term = term.slice(start + search.length + 1);
-                tally += start + search.length + 1;
-                start = term.indexOf(search);
-                positions.push(tally + start);
-            }
-            // remove the last position
-            if (positions.length > 1) {
-                positions.pop();
-            }
-        }
-        const placeholder = type === 'number' ? '0' : contents === '' ? '--' : 'undefined';
-        return (
-            <div
-                className="collectionSchemaView-cellContainer"
-                style={{ cursor: field instanceof Doc ? 'grab' : 'auto' }}
-                ref={dragRef}
-                onPointerDown={this.onPointerDown}
-                onClick={action(e => (this._isEditing = true))}
-                onPointerEnter={onPointerEnter}
-                onPointerLeave={onPointerLeave}>
-                <div className={className} ref={this._focusRef} tabIndex={-1}>
-                    <div className="collectionSchemaView-cellContents" ref={type === undefined || type === 'document' ? this.dropRef : null}>
-                        {!this.props.Document._searchDoc ? (
-                            <EditableView
-                                editing={this._isEditing}
-                                isEditingCallback={this.isEditingCallback}
-                                display={'inline'}
-                                contents={contents}
-                                height={'auto'}
-                                maxHeight={Number(MAX_ROW_HEIGHT)}
-                                placeholder={placeholder}
-                                GetValue={() => {
-                                    const cfield = ComputedField.WithoutComputed(() => FieldValue(field));
-                                    const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
-                                    const cfinalScript = cscript?.split('return')[cscript.split('return').length - 1];
-                                    return cscript ? (cfinalScript?.endsWith(';') ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : Field.IsField(cfield) ? Field.toScriptString(cfield) : '';
-                                }}
-                                SetValue={action((value: string) => {
-                                    // sets what is displayed after the user makes an input
-                                    let retVal = false;
-                                    if (value.startsWith(':=') || value.startsWith('=:=')) {
-                                        // decides how to compute a value when given either of the above strings
-                                        const script = value.substring(value.startsWith('=:=') ? 3 : 2);
-                                        retVal = this.props.setComputed(script, value.startsWith(':=') ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col);
-                                    } else {
-                                        // check if the input is a number
-                                        let inputIsNum = true;
-                                        for (const s of value) {
-                                            if (isNaN(parseInt(s)) && !(s === '.') && !(s === ',')) {
-                                                inputIsNum = false;
-                                            }
-                                        }
-                                        // check if the input is a boolean
-                                        const inputIsBool: boolean = value === 'false' || value === 'true';
-                                        // what to do in the case
-                                        if (!inputIsNum && !inputIsBool && !value.startsWith('=')) {
-                                            // if it's not a number, it's a string, and should be processed as such
-                                            // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically
-                                            // after each edit
-                                            let valueSansQuotes = value;
-                                            if (this._isEditing) {
-                                                const vsqLength = valueSansQuotes.length;
-                                                // get rid of outer quotes
-                                                valueSansQuotes = valueSansQuotes.substring(value.startsWith('"') ? 1 : 0, valueSansQuotes.charAt(vsqLength - 1) === '"' ? vsqLength - 1 : vsqLength);
-                                            }
-                                            let inputAsString = '"';
-                                            // escape any quotes in the string
-                                            for (const i of valueSansQuotes) {
-                                                if (i === '"') {
-                                                    inputAsString += '\\"';
-                                                } else {
-                                                    inputAsString += i;
-                                                }
-                                            }
-                                            // add a closing quote
-                                            inputAsString += '"';
-                                            //two options here: we can strip off outer quotes or we can figure out what's going on with the script
-                                            const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                            const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length;
-                                            // change it if a change is made, otherwise, just compile using the old cell conetnts
-                                            script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
-                                            // handle numbers and expressions
-                                        } else if (inputIsNum || value.startsWith('=')) {
-                                            //TODO: make accept numbers
-                                            const inputscript = value.substring(value.startsWith('=') ? 1 : 0);
-                                            // if commas are not stripped, the parser only considers the numbers after the last comma
-                                            let inputSansCommas = '';
-                                            for (const s of inputscript) {
-                                                if (!(s === ',')) {
-                                                    inputSansCommas += s;
-                                                }
-                                            }
-                                            const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                            const changeMade = value.length - 2 !== value.length;
-                                            script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
-                                            // handle booleans
-                                        } else if (inputIsBool) {
-                                            const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                            const changeMade = value.length - 2 !== value.length;
-                                            script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
-                                        }
-                                    }
-                                    if (retVal) {
-                                        this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing'
-                                        this.props.setIsEditing(false);
-                                    }
-                                    return retVal;
-                                })}
-                                OnFillDown={async (value: string) => {
-                                    // computes all of the value preceded by :=
-                                    const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
-                                    script.compiled &&
-                                        DocListCast(this.props.Document[this.props.fieldKey]).forEach((doc, i) =>
-                                            value.startsWith(':=') ? this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run)
-                                        );
-                                }}
-                            />
-                        ) : (
-                            this.returnHighlights(contents, positions)
-                        )}
-                    </div>
-                </div>
-            </div>
-        );
-    }
-
-    render() {
-        return this.renderCellWithType(undefined);
-    }
-}
-
-@observer
-export class CollectionSchemaNumberCell extends CollectionSchemaCell {
-    render() {
-        return this.renderCellWithType('number');
-    }
-}
-
-@observer
-export class CollectionSchemaBooleanCell extends CollectionSchemaCell {
-    render() {
-        return this.renderCellWithType('boolean');
-    }
-}
-
-@observer
-export class CollectionSchemaStringCell extends CollectionSchemaCell {
-    render() {
-        return this.renderCellWithType('string');
-    }
-}
-
-@observer
-export class CollectionSchemaDateCell extends CollectionSchemaCell {
-    @computed get _date(): Opt<DateField> {
-        // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
-        return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined;
-    }
-
-    @action
-    handleChange = (date: any) => {
-        // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } });
-        // if (script.compiled) {
-        //     this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
-        // } else {
-        // ^ DateCast is always undefined for some reason, but that is what the field should be set to
-        this._rowDoc[this.renderFieldKey] = new DateField(date as Date);
-        //}
-    };
-
-    render() {
-        return !this.props.isFocused ? (
-            <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : '--'}</span>
-        ) : (
-            <DatePicker selected={this._date?.date || new Date()} onSelect={date => this.handleChange(date)} onChange={date => this.handleChange(date)} />
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaDocCell extends CollectionSchemaCell {
-    _overlayDisposer?: () => void;
-
-    @computed get _doc() {
-        return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc));
-    }
-
-    @action
-    onSetValue = (value: string) => {
-        this._doc && (Doc.GetProto(this._doc).title = value);
-
-        const script = CompileScript(value, {
-            addReturn: true,
-            typecheck: true,
-            transformer: DocumentIconContainer.getTransformer(),
-        });
-        // compile the script
-        const results = script.compiled && script.run();
-        // if the script was compiled and run
-        if (results && results.success) {
-            this._rowDoc[this.renderFieldKey] = results.result;
-            return true;
-        }
-        return false;
-    };
-
-    componentWillUnmount() {
-        this.onBlur();
-    }
-
-    onBlur = () => {
-        this._overlayDisposer?.();
-    };
-    onFocus = () => {
-        this.onBlur();
-        this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
-    };
-
-    @action
-    isEditingCallback = (isEditing: boolean): void => {
-        // the isEditingCallback from a general CollectionSchemaCell
-        document.removeEventListener('keydown', this.onKeyDown);
-        isEditing && document.addEventListener('keydown', this.onKeyDown);
-        this._isEditing = isEditing;
-        this.props.setIsEditing(isEditing);
-        this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
-    };
-
-    render() {
-        // if there's a doc, render it
-        return !this._doc ? (
-            this.renderCellWithType('document')
-        ) : (
-            <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                <div className="collectionSchemaView-cellContents-document" style={{ padding: '5.9px' }} ref={this.dropRef} onFocus={this.onFocus} onBlur={this.onBlur}>
-                    <EditableView
-                        editing={this._isEditing}
-                        isEditingCallback={this.isEditingCallback}
-                        display={'inline'}
-                        contents={this._doc.title || '--'}
-                        height={'auto'}
-                        maxHeight={Number(MAX_ROW_HEIGHT)}
-                        GetValue={() => StrCast(this._doc?.title)}
-                        SetValue={action((value: string) => {
-                            this.onSetValue(value);
-                            return true;
-                        })}
-                    />
-                </div>
-                <div onClick={() => this._doc && this.props.addDocTab(this._doc, OpenWhere.addRight)} className="collectionSchemaView-cellContents-docButton">
-                    <FontAwesomeIcon icon="external-link-alt" size="lg" />
-                </div>
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaImageCell extends CollectionSchemaCell {
-    choosePath(url: URL) {
-        if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
-        if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
-        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question
-
-        const ext = extname(url.href);
-        return url.href.replace(ext, '_o' + ext);
-    }
-
-    render() {
-        const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
-        const alts = DocListCast(this._rowDoc[this.renderFieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
-        const altpaths = alts
-            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
-            .filter(url => url)
-            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
-        const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
-        // If there is a path, follow it; otherwise, follow a link to a default image icon
-        const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
-
-        const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio
-        let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px
-        const height = Math.min(75, width / aspect); // get a height either proportional to that or 75 px
-        width = height * aspect; // increase the width of the image if necessary to maintain proportionality
-
-        const reference = React.createRef<HTMLDivElement>();
-        return (
-            <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
-                    <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} />
-                </div>
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaListCell extends CollectionSchemaCell {
-    _overlayDisposer?: () => void;
-
-    @computed get _field() {
-        return this._rowDoc[this.renderFieldKey];
-    }
-    @computed get _optionsList() {
-        return this._field as List<any>;
-    }
-    @observable private _opened = false; // whether the list is opened
-    @observable private _text = 'select an item';
-    @observable private _selectedNum = 0; // the index of the list item selected
-
-    @action
-    onSetValue = (value: string) => {
-        // change if it's a document
-        this._optionsList[this._selectedNum] = this._text = value;
-
-        (this._field as List<any>).splice(this._selectedNum, 1, value);
-    };
-
-    @action
-    onSelected = (element: string, index: number) => {
-        // if an item is selected, the private variables should update to reflect this
-        this._text = element;
-        this._selectedNum = index;
-    };
-
-    onFocus = () => {
-        this._overlayDisposer?.();
-        this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
-    };
-
-    render() {
-        const link = false;
-        const reference = React.createRef<HTMLDivElement>();
-
-        // if the list is not opened, don't display it; otherwise, do.
-        if (this._optionsList?.length) {
-            const options = !this._opened ? null : (
-                <div>
-                    {this._optionsList.map((element, index) => {
-                        const val = Field.toString(element);
-                        return (
-                            <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: '6px' }} onPointerDown={e => this.onSelected(StrCast(element), index)}>
-                                {val}
-                            </div>
-                        );
-                    })}
-                </div>
-            );
-
-            const plainText = <div style={{ padding: '5.9px' }}>{this._text}</div>;
-            const textarea = (
-                <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: '5.9px' }} ref={this.dropRef}>
-                    <EditableView
-                        editing={this._isEditing}
-                        isEditingCallback={this.isEditingCallback}
-                        display={'inline'}
-                        contents={this._text}
-                        height={'auto'}
-                        maxHeight={Number(MAX_ROW_HEIGHT)}
-                        GetValue={() => this._text}
-                        SetValue={action((value: string) => {
-                            // add special for params
-                            this.onSetValue(value);
-                            return true;
-                        })}
-                    />
-                </div>
-            );
-
-            //☰
-            return (
-                <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                    <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
-                        <div className="collectionSchemaView-dropDownWrapper">
-                            <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: 'length', position: 'relative' }} onClick={action(e => (this._opened = !this._opened))}>
-                                <FontAwesomeIcon icon={this._opened ? 'caret-up' : 'caret-down'} size="sm" />
-                            </button>
-                            <div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
-                        </div>
-                        {options}
-                    </div>
-                </div>
-            );
-        }
-        return this.renderCellWithType('list');
-    }
-}
-
-@observer
-export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
-    @computed get _isChecked() {
-        return BoolCast(this._rowDoc[this.renderFieldKey]);
-    }
-
-    render() {
-        const reference = React.createRef<HTMLDivElement>();
-        return (
-            <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
-                <input type="checkbox" checked={this._isChecked} onChange={e => (this._rowDoc[this.renderFieldKey] = e.target.checked)} />
-            </div>
-        );
-    }
-}
-
-@observer
-export class CollectionSchemaButtons extends CollectionSchemaCell {
-    // the navigation buttons for schema view when it is used for search.
-    render() {
-        return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? (
-            <></>
-        ) : (
-            <div style={{ paddingTop: 8, paddingLeft: 3 }}>
-                <button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}>
-                    <FontAwesomeIcon icon="arrow-up" size="sm" />
-                </button>
-                <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)}>
-                    <FontAwesomeIcon icon="arrow-down" size="sm" />
-                </button>
-            </div>
-        );
-    }
-}
+// import React = require('react');
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action, computed, observable } from 'mobx';
+// import { observer } from 'mobx-react';
+// import { extname } from 'path';
+// import DatePicker from 'react-datepicker';
+// import { CellInfo } from 'react-table';
+// import { DateField } from '../../../../fields/DateField';
+// import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { List } from '../../../../fields/List';
+// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { ComputedField } from '../../../../fields/ScriptField';
+// import { BoolCast, Cast, DateCast, FieldValue, StrCast } from '../../../../fields/Types';
+// import { ImageField } from '../../../../fields/URLField';
+// import { emptyFunction, Utils } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { DocumentType } from '../../../documents/DocumentTypes';
+// import { DocumentManager } from '../../../util/DocumentManager';
+// import { DragManager } from '../../../util/DragManager';
+// import { KeyCodes } from '../../../util/KeyCodes';
+// import { CompileScript } from '../../../util/Scripting';
+// import { SearchUtil } from '../../../util/SearchUtil';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { undoBatch } from '../../../util/UndoManager';
+// import '../../../views/DocumentDecorations.scss';
+// import { EditableView } from '../../EditableView';
+// import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
+// import { DocumentIconContainer } from '../../nodes/DocumentIcon';
+// import { OverlayView } from '../../OverlayView';
+// import { CollectionView } from '../CollectionView';
+// import './CollectionSchemaView.scss';
+// import { OpenWhere } from '../../nodes/DocumentView';
+// import { PinProps } from '../../nodes/trails';
+
+// // intialize cell properties
+// export interface CellProps {
+//     row: number;
+//     col: number;
+//     rowProps: CellInfo;
+//     // currently unused
+//     CollectionView: Opt<CollectionView>;
+//     // currently unused
+//     ContainingCollection: Opt<CollectionView>;
+//     Document: Doc;
+//     // column name
+//     fieldKey: string;
+//     // currently unused
+//     renderDepth: number;
+//     // called when a button is pressed on the node itself
+//     addDocTab: (document: Doc, where: OpenWhere) => boolean;
+//     pinToPres: (document: Doc, pinProps: PinProps) => void;
+//     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+//     isFocused: boolean;
+//     changeFocusedCellByIndex: (row: number, col: number) => void;
+//     // set whether the cell is in the isEditing mode
+//     setIsEditing: (isEditing: boolean) => void;
+//     isEditable: boolean;
+//     setPreviewDoc: (doc: Doc) => void;
+//     setComputed: (script: string, doc: Doc, field: string, row: number, col: number) => boolean;
+//     getField: (row: number, col?: number) => void;
+//     // currnetly unused
+//     showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void;
+// }
+
+// @observer
+// export class CollectionSchemaCell extends React.Component<CellProps> {
+//     // return a field key that is corrected for whether it COMMENT
+//     public static resolvedFieldKey(column: string, rowDoc: Doc) {
+//         const fieldKey = column;
+//         if (fieldKey.startsWith('*')) {
+//             const rootKey = fieldKey.substring(1);
+//             const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))];
+//             const matchedKeys = allKeys.filter(key => key.includes(rootKey));
+//             if (matchedKeys.length) return matchedKeys[0];
+//         }
+//         return fieldKey;
+//     }
+//     @observable protected _isEditing: boolean = false;
+//     protected _focusRef = React.createRef<HTMLDivElement>();
+//     protected _rowDoc = this.props.rowProps.original;
+//     // Gets the serialized data in proto form of the base proto that this document's proto inherits from
+//     protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original);
+//     // methods for dragging and dropping
+//     protected _dropDisposer?: DragManager.DragDropDisposer;
+//     @observable contents: string = '';
+
+//     componentDidMount() {
+//         document.addEventListener('keydown', this.onKeyDown);
+//     }
+//     componentWillUnmount() {
+//         document.removeEventListener('keydown', this.onKeyDown);
+//     }
+
+//     @action
+//     onKeyDown = (e: KeyboardEvent): void => {
+//         // If a cell is editable and clicked, hitting enter shoudl allow the user to edit it
+//         if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) {
+//             document.removeEventListener('keydown', this.onKeyDown);
+//             this._isEditing = true;
+//             this.props.setIsEditing(true);
+//         }
+//     };
+
+//     @action
+//     isEditingCallback = (isEditing: boolean): void => {
+//         // a general method that takes a boolean that determines whether the cell should be in
+//         // is-editing mode
+//         // remove the event listener if it's there
+//         document.removeEventListener('keydown', this.onKeyDown);
+//         // it's not already in is-editing mode, re-add the event listener
+//         isEditing && document.addEventListener('keydown', this.onKeyDown);
+//         this._isEditing = isEditing;
+//         this.props.setIsEditing(isEditing);
+//         this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
+//     };
+
+//     @action
+//     onPointerDown = async (e: React.PointerEvent): Promise<void> => {
+//         // pan to the cell
+//         this.onItemDown(e);
+//         // focus on it
+//         this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
+//         this.props.setPreviewDoc(this.props.rowProps.original);
+
+//         let url: string;
+//         if ((url = StrCast(this.props.rowProps.row.href))) {
+//             // opens up the the doc in a new window, blurring the old one
+//             try {
+//                 new URL(url);
+//                 const temp = window.open(url)!;
+//                 temp.blur();
+//                 window.focus();
+//             } catch {}
+//         }
+
+//         const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null);
+//         doc && this.props.setPreviewDoc(doc);
+//     };
+
+//     @undoBatch
+//     applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => {
+//         // apply a specified change to the cell
+//         const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) });
+//         if (!res.success) return false;
+//         // change what is rendered to this new changed cell content
+//         doc[this.renderFieldKey] = res.result;
+//         return true;
+//         // return whether the change was successful
+//     };
+
+//     private drop = (e: Event, de: DragManager.DropEvent) => {
+//         // if the drag has data at its completion
+//         if (de.complete.docDragData) {
+//             // if only one doc was dragged
+//             if (de.complete.docDragData.draggedDocuments.length === 1) {
+//                 // update the renderFieldKey
+//                 this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
+//             } else {
+//                 // create schema document reflecting the new column arrangement
+//                 const coll = Docs.Create.SchemaDocument([new SchemaHeaderField('title', '#f1efeb')], de.complete.docDragData.draggedDocuments, {});
+//                 this._rowDataDoc[this.renderFieldKey] = coll;
+//             }
+//             e.stopPropagation();
+//         }
+//     };
+
+//     protected dropRef = (ele: HTMLElement | null) => {
+//         // if the drop disposer is not undefined, run its function
+//         this._dropDisposer?.();
+//         // if ele is not null, give ele a non-undefined drop disposer
+//         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
+//     };
+
+//     returnHighlights(contents: string, positions?: number[]) {
+//         if (positions) {
+//             const results = [];
+//             StrCast(this.props.Document._searchString);
+//             const length = StrCast(this.props.Document._searchString).length;
+//             const color = contents ? 'black' : 'grey';
+
+//             results.push(
+//                 <span key="-1" style={{ color }}>
+//                     {contents?.slice(0, positions[0])}
+//                 </span>
+//             );
+//             positions.forEach((num, cur) => {
+//                 results.push(
+//                     <span key={'start' + cur} style={{ backgroundColor: '#FFFF00', color }}>
+//                         {contents?.slice(num, num + length)}
+//                     </span>
+//                 );
+//                 let end = 0;
+//                 cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]);
+//                 results.push(
+//                     <span key={'end' + cur} style={{ color }}>
+//                         {contents?.slice(num + length, end)}
+//                     </span>
+//                 );
+//             });
+//             return results;
+//         }
+//         return <span style={{ color: contents ? 'black' : 'grey' }}>{contents ? contents?.valueOf() : 'undefined'}</span>;
+//     }
+
+//     @computed get renderFieldKey() {
+//         // gets the resolved field key of this cell
+//         return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original);
+//     }
+
+//     onItemDown = async (e: React.PointerEvent) => {
+//         // if the document is a document used to change UI for search results in schema view
+//         if (this.props.Document._searchDoc) {
+//             const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
+//             const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
+//             // Jump to the this document
+//             DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc));
+//         }
+//     };
+
+//     renderCellWithType(type: string | undefined) {
+//         const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+
+//         // the column
+//         const fieldKey = this.renderFieldKey;
+//         // the exact cell
+//         const field = this._rowDoc[fieldKey];
+
+//         const onPointerEnter = (e: React.PointerEvent): void => {
+//             // e.buttons === 1 means the left moue pointer is down
+//             if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === 'document' || type === undefined)) {
+//                 dragRef.current!.className = 'collectionSchemaView-cellContainer doc-drag-over';
+//             }
+//         };
+//         const onPointerLeave = (e: React.PointerEvent): void => {
+//             // change the class name to indicate that the cell is no longer being dragged
+//             dragRef.current!.className = 'collectionSchemaView-cellContainer';
+//         };
+
+//         let contents = Field.toString(field as Field);
+//         // display 2 hyphens instead of a blank box for empty cells
+//         contents = contents === '' ? '--' : contents;
+
+//         // classname reflects the tatus of the cell
+//         let className = 'collectionSchemaView-cellWrapper';
+//         if (this._isEditing) className += ' editing';
+//         if (this.props.isFocused && this.props.isEditable) className += ' focused';
+//         if (this.props.isFocused && !this.props.isEditable) className += ' inactive';
+
+//         const positions = [];
+//         if (StrCast(this.props.Document._searchString).toLowerCase() !== '') {
+//             // term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents
+//             let term = field instanceof Promise ? '...promise pending...' : contents.toLowerCase();
+//             const search = StrCast(this.props.Document._searchString).toLowerCase();
+//             let start = term.indexOf(search);
+//             let tally = 0;
+//             // if search is found in term
+//             if (start !== -1) {
+//                 positions.push(start);
+//             }
+//             // if search is found in term, continue finding all instances of search in term
+//             while (start < contents?.length && start !== -1) {
+//                 term = term.slice(start + search.length + 1);
+//                 tally += start + search.length + 1;
+//                 start = term.indexOf(search);
+//                 positions.push(tally + start);
+//             }
+//             // remove the last position
+//             if (positions.length > 1) {
+//                 positions.pop();
+//             }
+//         }
+//         const placeholder = type === 'number' ? '0' : contents === '' ? '--' : 'undefined';
+//         return (
+//             <div
+//                 className="collectionSchemaView-cellContainer"
+//                 style={{ cursor: field instanceof Doc ? 'grab' : 'auto' }}
+//                 ref={dragRef}
+//                 onPointerDown={this.onPointerDown}
+//                 onClick={action(e => (this._isEditing = true))}
+//                 onPointerEnter={onPointerEnter}
+//                 onPointerLeave={onPointerLeave}>
+//                 <div className={className} ref={this._focusRef} tabIndex={-1}>
+//                     <div className="collectionSchemaView-cellContents" ref={type === undefined || type === 'document' ? this.dropRef : null}>
+//                         {!this.props.Document._searchDoc ? (
+//                             <EditableView
+//                                 editing={this._isEditing}
+//                                 isEditingCallback={this.isEditingCallback}
+//                                 display={'inline'}
+//                                 contents={contents}
+//                                 height={'auto'}
+//                                 maxHeight={Number(MAX_ROW_HEIGHT)}
+//                                 placeholder={placeholder}
+//                                 GetValue={() => {
+//                                     const cfield = ComputedField.WithoutComputed(() => FieldValue(field));
+//                                     const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
+//                                     const cfinalScript = cscript?.split('return')[cscript.split('return').length - 1];
+//                                     return cscript ? (cfinalScript?.endsWith(';') ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : Field.IsField(cfield) ? Field.toScriptString(cfield) : '';
+//                                 }}
+//                                 SetValue={action((value: string) => {
+//                                     // sets what is displayed after the user makes an input
+//                                     let retVal = false;
+//                                     if (value.startsWith(':=') || value.startsWith('=:=')) {
+//                                         // decides how to compute a value when given either of the above strings
+//                                         const script = value.substring(value.startsWith('=:=') ? 3 : 2);
+//                                         retVal = this.props.setComputed(script, value.startsWith(':=') ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col);
+//                                     } else {
+//                                         // check if the input is a number
+//                                         let inputIsNum = true;
+//                                         for (const s of value) {
+//                                             if (isNaN(parseInt(s)) && !(s === '.') && !(s === ',')) {
+//                                                 inputIsNum = false;
+//                                             }
+//                                         }
+//                                         // check if the input is a boolean
+//                                         const inputIsBool: boolean = value === 'false' || value === 'true';
+//                                         // what to do in the case
+//                                         if (!inputIsNum && !inputIsBool && !value.startsWith('=')) {
+//                                             // if it's not a number, it's a string, and should be processed as such
+//                                             // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically
+//                                             // after each edit
+//                                             let valueSansQuotes = value;
+//                                             if (this._isEditing) {
+//                                                 const vsqLength = valueSansQuotes.length;
+//                                                 // get rid of outer quotes
+//                                                 valueSansQuotes = valueSansQuotes.substring(value.startsWith('"') ? 1 : 0, valueSansQuotes.charAt(vsqLength - 1) === '"' ? vsqLength - 1 : vsqLength);
+//                                             }
+//                                             let inputAsString = '"';
+//                                             // escape any quotes in the string
+//                                             for (const i of valueSansQuotes) {
+//                                                 if (i === '"') {
+//                                                     inputAsString += '\\"';
+//                                                 } else {
+//                                                     inputAsString += i;
+//                                                 }
+//                                             }
+//                                             // add a closing quote
+//                                             inputAsString += '"';
+//                                             //two options here: we can strip off outer quotes or we can figure out what's going on with the script
+//                                             const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                             const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length;
+//                                             // change it if a change is made, otherwise, just compile using the old cell conetnts
+//                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
+//                                             // handle numbers and expressions
+//                                         } else if (inputIsNum || value.startsWith('=')) {
+//                                             //TODO: make accept numbers
+//                                             const inputscript = value.substring(value.startsWith('=') ? 1 : 0);
+//                                             // if commas are not stripped, the parser only considers the numbers after the last comma
+//                                             let inputSansCommas = '';
+//                                             for (const s of inputscript) {
+//                                                 if (!(s === ',')) {
+//                                                     inputSansCommas += s;
+//                                                 }
+//                                             }
+//                                             const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                             const changeMade = value.length - 2 !== value.length;
+//                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
+//                                             // handle booleans
+//                                         } else if (inputIsBool) {
+//                                             const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                             const changeMade = value.length - 2 !== value.length;
+//                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
+//                                         }
+//                                     }
+//                                     if (retVal) {
+//                                         this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing'
+//                                         this.props.setIsEditing(false);
+//                                     }
+//                                     return retVal;
+//                                 })}
+//                                 OnFillDown={async (value: string) => {
+//                                     // computes all of the value preceded by :=
+//                                     const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+//                                     script.compiled &&
+//                                         DocListCast(this.props.Document[this.props.fieldKey]).forEach((doc, i) =>
+//                                             value.startsWith(':=') ? this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run)
+//                                         );
+//                                 }}
+//                             />
+//                         ) : (
+//                             this.returnHighlights(contents, positions)
+//                         )}
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     }
+
+//     render() {
+//         return this.renderCellWithType(undefined);
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaNumberCell extends CollectionSchemaCell {
+//     render() {
+//         return this.renderCellWithType('number');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaBooleanCell extends CollectionSchemaCell {
+//     render() {
+//         return this.renderCellWithType('boolean');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaStringCell extends CollectionSchemaCell {
+//     render() {
+//         return this.renderCellWithType('string');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaDateCell extends CollectionSchemaCell {
+//     @computed get _date(): Opt<DateField> {
+//         // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
+//         return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined;
+//     }
+
+//     @action
+//     handleChange = (date: any) => {
+//         // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } });
+//         // if (script.compiled) {
+//         //     this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
+//         // } else {
+//         // ^ DateCast is always undefined for some reason, but that is what the field should be set to
+//         this._rowDoc[this.renderFieldKey] = new DateField(date as Date);
+//         //}
+//     };
+
+//     render() {
+//         return !this.props.isFocused ? (
+//             <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : '--'}</span>
+//         ) : (
+//             <DatePicker selected={this._date?.date || new Date()} onSelect={date => this.handleChange(date)} onChange={date => this.handleChange(date)} />
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaDocCell extends CollectionSchemaCell {
+//     _overlayDisposer?: () => void;
+
+//     @computed get _doc() {
+//         return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc));
+//     }
+
+//     @action
+//     onSetValue = (value: string) => {
+//         this._doc && (Doc.GetProto(this._doc).title = value);
+
+//         const script = CompileScript(value, {
+//             addReturn: true,
+//             typecheck: true,
+//             transformer: DocumentIconContainer.getTransformer(),
+//         });
+//         // compile the script
+//         const results = script.compiled && script.run();
+//         // if the script was compiled and run
+//         if (results && results.success) {
+//             this._rowDoc[this.renderFieldKey] = results.result;
+//             return true;
+//         }
+//         return false;
+//     };
+
+//     componentWillUnmount() {
+//         this.onBlur();
+//     }
+
+//     onBlur = () => {
+//         this._overlayDisposer?.();
+//     };
+//     onFocus = () => {
+//         this.onBlur();
+//         this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+//     };
+
+//     @action
+//     isEditingCallback = (isEditing: boolean): void => {
+//         // the isEditingCallback from a general CollectionSchemaCell
+//         document.removeEventListener('keydown', this.onKeyDown);
+//         isEditing && document.addEventListener('keydown', this.onKeyDown);
+//         this._isEditing = isEditing;
+//         this.props.setIsEditing(isEditing);
+//         this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
+//     };
+
+//     render() {
+//         // if there's a doc, render it
+//         return !this._doc ? (
+//             this.renderCellWithType('document')
+//         ) : (
+//             <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                 <div className="collectionSchemaView-cellContents-document" style={{ padding: '5.9px' }} ref={this.dropRef} onFocus={this.onFocus} onBlur={this.onBlur}>
+//                     <EditableView
+//                         editing={this._isEditing}
+//                         isEditingCallback={this.isEditingCallback}
+//                         display={'inline'}
+//                         contents={this._doc.title || '--'}
+//                         height={'auto'}
+//                         maxHeight={Number(MAX_ROW_HEIGHT)}
+//                         GetValue={() => StrCast(this._doc?.title)}
+//                         SetValue={action((value: string) => {
+//                             this.onSetValue(value);
+//                             return true;
+//                         })}
+//                     />
+//                 </div>
+//                 <div onClick={() => this._doc && this.props.addDocTab(this._doc, OpenWhere.addRight)} className="collectionSchemaView-cellContents-docButton">
+//                     <FontAwesomeIcon icon="external-link-alt" size="lg" />
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaImageCell extends CollectionSchemaCell {
+//     choosePath(url: URL) {
+//         if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
+//         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
+//         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question
+
+//         const ext = extname(url.href);
+//         return url.href.replace(ext, '_o' + ext);
+//     }
+
+//     render() {
+//         const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+//         const alts = DocListCast(this._rowDoc[this.renderFieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
+//         const altpaths = alts
+//             .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+//             .filter(url => url)
+//             .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+//         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
+//         // If there is a path, follow it; otherwise, follow a link to a default image icon
+//         const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
+
+//         const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio
+//         let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px
+//         const height = Math.min(75, width / aspect); // get a height either proportional to that or 75 px
+//         width = height * aspect; // increase the width of the image if necessary to maintain proportionality
+
+//         const reference = React.createRef<HTMLDivElement>();
+//         return (
+//             <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                 <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
+//                     <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} />
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaListCell extends CollectionSchemaCell {
+//     _overlayDisposer?: () => void;
+
+//     @computed get _field() {
+//         return this._rowDoc[this.renderFieldKey];
+//     }
+//     @computed get _optionsList() {
+//         return this._field as List<any>;
+//     }
+//     @observable private _opened = false; // whether the list is opened
+//     @observable private _text = 'select an item';
+//     @observable private _selectedNum = 0; // the index of the list item selected
+
+//     @action
+//     onSetValue = (value: string) => {
+//         // change if it's a document
+//         this._optionsList[this._selectedNum] = this._text = value;
+
+//         (this._field as List<any>).splice(this._selectedNum, 1, value);
+//     };
+
+//     @action
+//     onSelected = (element: string, index: number) => {
+//         // if an item is selected, the private variables should update to reflect this
+//         this._text = element;
+//         this._selectedNum = index;
+//     };
+
+//     onFocus = () => {
+//         this._overlayDisposer?.();
+//         this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+//     };
+
+//     render() {
+//         const link = false;
+//         const reference = React.createRef<HTMLDivElement>();
+
+//         // if the list is not opened, don't display it; otherwise, do.
+//         if (this._optionsList?.length) {
+//             const options = !this._opened ? null : (
+//                 <div>
+//                     {this._optionsList.map((element, index) => {
+//                         const val = Field.toString(element);
+//                         return (
+//                             <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: '6px' }} onPointerDown={e => this.onSelected(StrCast(element), index)}>
+//                                 {val}
+//                             </div>
+//                         );
+//                     })}
+//                 </div>
+//             );
+
+//             const plainText = <div style={{ padding: '5.9px' }}>{this._text}</div>;
+//             const textarea = (
+//                 <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: '5.9px' }} ref={this.dropRef}>
+//                     <EditableView
+//                         editing={this._isEditing}
+//                         isEditingCallback={this.isEditingCallback}
+//                         display={'inline'}
+//                         contents={this._text}
+//                         height={'auto'}
+//                         maxHeight={Number(MAX_ROW_HEIGHT)}
+//                         GetValue={() => this._text}
+//                         SetValue={action((value: string) => {
+//                             // add special for params
+//                             this.onSetValue(value);
+//                             return true;
+//                         })}
+//                     />
+//                 </div>
+//             );
+
+//             //☰
+//             return (
+//                 <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                     <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
+//                         <div className="collectionSchemaView-dropDownWrapper">
+//                             <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: 'length', position: 'relative' }} onClick={action(e => (this._opened = !this._opened))}>
+//                                 <FontAwesomeIcon icon={this._opened ? 'caret-up' : 'caret-down'} size="sm" />
+//                             </button>
+//                             <div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
+//                         </div>
+//                         {options}
+//                     </div>
+//                 </div>
+//             );
+//         }
+//         return this.renderCellWithType('list');
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
+//     @computed get _isChecked() {
+//         return BoolCast(this._rowDoc[this.renderFieldKey]);
+//     }
+
+//     render() {
+//         const reference = React.createRef<HTMLDivElement>();
+//         return (
+//             <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+//                 <input type="checkbox" checked={this._isChecked} onChange={e => (this._rowDoc[this.renderFieldKey] = e.target.checked)} />
+//             </div>
+//         );
+//     }
+// }
+
+// @observer
+// export class CollectionSchemaButtons extends CollectionSchemaCell {
+//     // the navigation buttons for schema view when it is used for search.
+//     render() {
+//         return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? (
+//             <></>
+//         ) : (
+//             <div style={{ paddingTop: 8, paddingLeft: 3 }}>
+//                 <button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}>
+//                     <FontAwesomeIcon icon="arrow-up" size="sm" />
+//                 </button>
+//                 <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)}>
+//                     <FontAwesomeIcon icon="arrow-down" size="sm" />
+//                 </button>
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
index 28d2e6ab1..c2182ae0c 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
@@ -1,138 +1,138 @@
-import React = require('react');
-import { action } from 'mobx';
-import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { DragManager } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import './CollectionSchemaView.scss';
+// import React = require('react');
+// import { action } from 'mobx';
+// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { DragManager } from '../../../util/DragManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { Transform } from '../../../util/Transform';
+// import './CollectionSchemaView.scss';
 
-export interface MovableColumnProps {
-    columnRenderer: React.ReactNode;
-    columnValue: SchemaHeaderField;
-    allColumns: SchemaHeaderField[];
-    reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void;
-    ScreenToLocalTransform: () => Transform;
-}
-export class MovableColumn extends React.Component<MovableColumnProps> {
-    // The header of the column
-    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
-    // The container of the function that is responsible for moving the column over to a new plac
-    private _colDropDisposer?: DragManager.DragDropDisposer;
-    // initial column position
-    private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 };
-    // sensitivity to being dragged, in pixels
-    private _sensitivity: number = 16;
-    // Column reference ID
-    private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
+// export interface MovableColumnProps {
+//     columnRenderer: React.ReactNode;
+//     columnValue: SchemaHeaderField;
+//     allColumns: SchemaHeaderField[];
+//     reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void;
+//     ScreenToLocalTransform: () => Transform;
+// }
+// export class MovableColumn extends React.Component<MovableColumnProps> {
+//     // The header of the column
+//     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+//     // The container of the function that is responsible for moving the column over to a new plac
+//     private _colDropDisposer?: DragManager.DragDropDisposer;
+//     // initial column position
+//     private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 };
+//     // sensitivity to being dragged, in pixels
+//     private _sensitivity: number = 16;
+//     // Column reference ID
+//     private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
 
-    onPointerEnter = (e: React.PointerEvent): void => {
-        // if the column is left-clicked and it is being dragged
-        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header!.current!.className = 'collectionSchema-col-wrapper';
-            document.addEventListener('pointermove', this.onDragMove, true);
-        }
-    };
+//     onPointerEnter = (e: React.PointerEvent): void => {
+//         // if the column is left-clicked and it is being dragged
+//         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+//             this._header!.current!.className = 'collectionSchema-col-wrapper';
+//             document.addEventListener('pointermove', this.onDragMove, true);
+//         }
+//     };
 
-    onPointerLeave = (e: React.PointerEvent): void => {
-        this._header!.current!.className = 'collectionSchema-col-wrapper';
-        document.removeEventListener('pointermove', this.onDragMove, true);
-        !e.buttons && document.removeEventListener('pointermove', this.onPointerMove);
-    };
+//     onPointerLeave = (e: React.PointerEvent): void => {
+//         this._header!.current!.className = 'collectionSchema-col-wrapper';
+//         document.removeEventListener('pointermove', this.onDragMove, true);
+//         !e.buttons && document.removeEventListener('pointermove', this.onPointerMove);
+//     };
 
-    onDragMove = (e: PointerEvent): void => {
-        // only take into account the horizonal direction when a column is dragged
-        const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        const rect = this._header!.current!.getBoundingClientRect();
-        // Now store the point at the top center of the column when it was in its original position
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
-        // to be compared with its new horizontal position
-        const before = x[0] < bounds[0];
-        this._header!.current!.className = 'collectionSchema-col-wrapper';
-        if (before) this._header!.current!.className += ' col-before';
-        if (!before) this._header!.current!.className += ' col-after';
-        e.stopPropagation();
-    };
+//     onDragMove = (e: PointerEvent): void => {
+//         // only take into account the horizonal direction when a column is dragged
+//         const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         // Now store the point at the top center of the column when it was in its original position
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
+//         // to be compared with its new horizontal position
+//         const before = x[0] < bounds[0];
+//         this._header!.current!.className = 'collectionSchema-col-wrapper';
+//         if (before) this._header!.current!.className += ' col-before';
+//         if (!before) this._header!.current!.className += ' col-after';
+//         e.stopPropagation();
+//     };
 
-    createColDropTarget = (ele: HTMLDivElement) => {
-        this._colDropDisposer?.();
-        if (ele) {
-            this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this));
-        }
-    };
+//     createColDropTarget = (ele: HTMLDivElement) => {
+//         this._colDropDisposer?.();
+//         if (ele) {
+//             this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this));
+//         }
+//     };
 
-    colDrop = (e: Event, de: DragManager.DropEvent) => {
-        document.removeEventListener('pointermove', this.onDragMove, true);
-        // we only care about whether the column is shifted to the side
-        const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
-        // get the dimensions of the smallest rectangle that bounds the header
-        const rect = this._header!.current!.getBoundingClientRect();
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
-        // get whether the column was dragged before or after where it is now
-        const before = x[0] < bounds[0];
-        const colDragData = de.complete.columnDragData;
-        // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables
-        if (colDragData) {
-            e.stopPropagation();
-            this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns);
-            return true;
-        }
-        return false;
-    };
+//     colDrop = (e: Event, de: DragManager.DropEvent) => {
+//         document.removeEventListener('pointermove', this.onDragMove, true);
+//         // we only care about whether the column is shifted to the side
+//         const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+//         // get the dimensions of the smallest rectangle that bounds the header
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
+//         // get whether the column was dragged before or after where it is now
+//         const before = x[0] < bounds[0];
+//         const colDragData = de.complete.columnDragData;
+//         // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables
+//         if (colDragData) {
+//             e.stopPropagation();
+//             this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns);
+//             return true;
+//         }
+//         return false;
+//     };
 
-    onPointerMove = (e: PointerEvent) => {
-        const onRowMove = (e: PointerEvent) => {
-            e.stopPropagation();
-            e.preventDefault();
+//     onPointerMove = (e: PointerEvent) => {
+//         const onRowMove = (e: PointerEvent) => {
+//             e.stopPropagation();
+//             e.preventDefault();
 
-            document.removeEventListener('pointermove', onRowMove);
-            document.removeEventListener('pointerup', onRowUp);
-            const dragData = new DragManager.ColumnDragData(this.props.columnValue);
-            DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y);
-        };
-        const onRowUp = (): void => {
-            document.removeEventListener('pointermove', onRowMove);
-            document.removeEventListener('pointerup', onRowUp);
-        };
-        // if the left mouse button is the one being held
-        if (e.buttons === 1) {
-            const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
-            // If the movemnt of the drag exceeds the sensitivity value
-            if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
-                document.removeEventListener('pointermove', this.onPointerMove);
-                e.stopPropagation();
+//             document.removeEventListener('pointermove', onRowMove);
+//             document.removeEventListener('pointerup', onRowUp);
+//             const dragData = new DragManager.ColumnDragData(this.props.columnValue);
+//             DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y);
+//         };
+//         const onRowUp = (): void => {
+//             document.removeEventListener('pointermove', onRowMove);
+//             document.removeEventListener('pointerup', onRowUp);
+//         };
+//         // if the left mouse button is the one being held
+//         if (e.buttons === 1) {
+//             const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
+//             // If the movemnt of the drag exceeds the sensitivity value
+//             if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
+//                 document.removeEventListener('pointermove', this.onPointerMove);
+//                 e.stopPropagation();
 
-                document.addEventListener('pointermove', onRowMove);
-                document.addEventListener('pointerup', onRowUp);
-            }
-        }
-    };
+//                 document.addEventListener('pointermove', onRowMove);
+//                 document.addEventListener('pointerup', onRowUp);
+//             }
+//         }
+//     };
 
-    onPointerUp = (e: React.PointerEvent) => {
-        document.removeEventListener('pointermove', this.onPointerMove);
-    };
+//     onPointerUp = (e: React.PointerEvent) => {
+//         document.removeEventListener('pointermove', this.onPointerMove);
+//     };
 
-    @action
-    onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
-        this._dragRef = ref;
-        const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
-        // If the cell thing dragged is not being edited
-        if (!(e.target as any)?.tagName.includes('INPUT')) {
-            this._startDragPosition = { x: dx, y: dy };
-            document.addEventListener('pointermove', this.onPointerMove);
-        }
-    };
+//     @action
+//     onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
+//         this._dragRef = ref;
+//         const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
+//         // If the cell thing dragged is not being edited
+//         if (!(e.target as any)?.tagName.includes('INPUT')) {
+//             this._startDragPosition = { x: dx, y: dy };
+//             document.addEventListener('pointermove', this.onPointerMove);
+//         }
+//     };
 
-    render() {
-        const reference = React.createRef<HTMLDivElement>();
+//     render() {
+//         const reference = React.createRef<HTMLDivElement>();
 
-        return (
-            <div className="collectionSchema-col" ref={this.createColDropTarget}>
-                <div className="collectionSchema-col-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                    <div className="col-dragger" ref={reference} onPointerDown={e => this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}>
-                        {this.props.columnRenderer}
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
+//         return (
+//             <div className="collectionSchema-col" ref={this.createColDropTarget}>
+//                 <div className="collectionSchema-col-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+//                     <div className="col-dragger" ref={reference} onPointerDown={e => this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}>
+//                         {this.props.columnRenderer}
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
index 3cb2df7d3..2b39df201 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
@@ -1,152 +1,152 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action } from 'mobx';
-import * as React from 'react';
-import { ReactTableDefaults, RowInfo } from 'react-table';
-import { Doc } from '../../../../fields/Doc';
-import { Cast, FieldValue, StrCast } from '../../../../fields/Types';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
-import { ContextMenu } from '../../ContextMenu';
-import { OpenWhere } from '../../nodes/DocumentView';
-import './CollectionSchemaView.scss';
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action } from 'mobx';
+// import * as React from 'react';
+// import { ReactTableDefaults, RowInfo } from 'react-table';
+// import { Doc } from '../../../../fields/Doc';
+// import { Cast, FieldValue, StrCast } from '../../../../fields/Types';
+// import { DocumentManager } from '../../../util/DocumentManager';
+// import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { Transform } from '../../../util/Transform';
+// import { undoBatch } from '../../../util/UndoManager';
+// import { ContextMenu } from '../../ContextMenu';
+// import { OpenWhere } from '../../nodes/DocumentView';
+// import './CollectionSchemaView.scss';
 
-export interface MovableRowProps {
-    rowInfo: RowInfo;
-    ScreenToLocalTransform: () => Transform;
-    addDoc: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
-    removeDoc: (doc: Doc | Doc[]) => boolean;
-    rowFocused: boolean;
-    textWrapRow: (doc: Doc) => void;
-    rowWrapped: boolean;
-    dropAction: string;
-    addDocTab: any;
-}
+// export interface MovableRowProps {
+//     rowInfo: RowInfo;
+//     ScreenToLocalTransform: () => Transform;
+//     addDoc: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean;
+//     removeDoc: (doc: Doc | Doc[]) => boolean;
+//     rowFocused: boolean;
+//     textWrapRow: (doc: Doc) => void;
+//     rowWrapped: boolean;
+//     dropAction: string;
+//     addDocTab: any;
+// }
 
-export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> {
-    private _header?: React.RefObject<HTMLDivElement> = React.createRef();
-    private _rowDropDisposer?: DragManager.DragDropDisposer;
+// export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> {
+//     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
+//     private _rowDropDisposer?: DragManager.DragDropDisposer;
 
-    // Event listeners are only necessary when the user is hovering over the table
-    // Create one when the mouse starts hovering...
-    onPointerEnter = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header!.current!.className = 'collectionSchema-row-wrapper';
-            document.addEventListener('pointermove', this.onDragMove, true);
-        }
-    };
-    // ... and delete it when the mouse leaves
-    onPointerLeave = (e: React.PointerEvent): void => {
-        this._header!.current!.className = 'collectionSchema-row-wrapper';
-        document.removeEventListener('pointermove', this.onDragMove, true);
-    };
-    // The method for the event listener, reorders columns when dragged to their new locations.
-    onDragMove = (e: PointerEvent): void => {
-        const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
-        const rect = this._header!.current!.getBoundingClientRect();
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
-        const before = x[1] < bounds[1];
-        this._header!.current!.className = 'collectionSchema-row-wrapper';
-        if (before) this._header!.current!.className += ' row-above';
-        if (!before) this._header!.current!.className += ' row-below';
-        e.stopPropagation();
-    };
-    componentWillUnmount() {
-        this._rowDropDisposer?.();
-    }
-    //
-    createRowDropTarget = (ele: HTMLDivElement) => {
-        this._rowDropDisposer?.();
-        if (ele) {
-            this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
-        }
-    };
-    // Controls what hppens when a row is dragged and dropped
-    rowDrop = (e: Event, de: DragManager.DropEvent) => {
-        this.onPointerLeave(e as any);
-        const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc));
-        if (!rowDoc) return false;
+//     // Event listeners are only necessary when the user is hovering over the table
+//     // Create one when the mouse starts hovering...
+//     onPointerEnter = (e: React.PointerEvent): void => {
+//         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+//             this._header!.current!.className = 'collectionSchema-row-wrapper';
+//             document.addEventListener('pointermove', this.onDragMove, true);
+//         }
+//     };
+//     // ... and delete it when the mouse leaves
+//     onPointerLeave = (e: React.PointerEvent): void => {
+//         this._header!.current!.className = 'collectionSchema-row-wrapper';
+//         document.removeEventListener('pointermove', this.onDragMove, true);
+//     };
+//     // The method for the event listener, reorders columns when dragged to their new locations.
+//     onDragMove = (e: PointerEvent): void => {
+//         const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+//         const before = x[1] < bounds[1];
+//         this._header!.current!.className = 'collectionSchema-row-wrapper';
+//         if (before) this._header!.current!.className += ' row-above';
+//         if (!before) this._header!.current!.className += ' row-below';
+//         e.stopPropagation();
+//     };
+//     componentWillUnmount() {
+//         this._rowDropDisposer?.();
+//     }
+//     //
+//     createRowDropTarget = (ele: HTMLDivElement) => {
+//         this._rowDropDisposer?.();
+//         if (ele) {
+//             this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
+//         }
+//     };
+//     // Controls what hppens when a row is dragged and dropped
+//     rowDrop = (e: Event, de: DragManager.DropEvent) => {
+//         this.onPointerLeave(e as any);
+//         const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc));
+//         if (!rowDoc) return false;
 
-        const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
-        const rect = this._header!.current!.getBoundingClientRect();
-        const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
-        const before = x[1] < bounds[1];
+//         const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+//         const rect = this._header!.current!.getBoundingClientRect();
+//         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
+//         const before = x[1] < bounds[1];
 
-        const docDragData = de.complete.docDragData;
-        if (docDragData) {
-            e.stopPropagation();
-            if (docDragData.draggedDocuments[0] === rowDoc) return true;
-            const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before);
-            const movedDocs = docDragData.draggedDocuments;
-            return docDragData.dropAction || docDragData.userDropAction
-                ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
-                : docDragData.moveDocument
-                ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
-                : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
-        }
-        return false;
-    };
+//         const docDragData = de.complete.docDragData;
+//         if (docDragData) {
+//             e.stopPropagation();
+//             if (docDragData.draggedDocuments[0] === rowDoc) return true;
+//             const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before);
+//             const movedDocs = docDragData.draggedDocuments;
+//             return docDragData.dropAction || docDragData.userDropAction
+//                 ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
+//                 : docDragData.moveDocument
+//                 ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
+//                 : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
+//         }
+//         return false;
+//     };
 
-    onRowContextMenu = (e: React.MouseEvent): void => {
-        const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row';
-        ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' });
-    };
+//     onRowContextMenu = (e: React.MouseEvent): void => {
+//         const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row';
+//         ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' });
+//     };
 
-    @undoBatch
-    @action
-    move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => {
-        const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
-        return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
-    };
+//     @undoBatch
+//     @action
+//     move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => {
+//         const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
+//         return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
+//     };
 
-    @action
-    onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        console.log('yes');
-        if (e.key === 'Backspace' || e.key === 'Delete') {
-            undoBatch(() => this.props.removeDoc(this.props.rowInfo.original));
-        }
-    };
+//     @action
+//     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+//         console.log('yes');
+//         if (e.key === 'Backspace' || e.key === 'Delete') {
+//             undoBatch(() => this.props.removeDoc(this.props.rowInfo.original));
+//         }
+//     };
 
-    render() {
-        const { children = null, rowInfo } = this.props;
+//     render() {
+//         const { children = null, rowInfo } = this.props;
 
-        if (!rowInfo) {
-            return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
-        }
+//         if (!rowInfo) {
+//             return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
+//         }
 
-        const { original } = rowInfo;
-        const doc = FieldValue(Cast(original, Doc));
+//         const { original } = rowInfo;
+//         const doc = FieldValue(Cast(original, Doc));
 
-        if (!doc) return null;
+//         if (!doc) return null;
 
-        const reference = React.createRef<HTMLDivElement>();
-        const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
+//         const reference = React.createRef<HTMLDivElement>();
+//         const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
 
-        let className = 'collectionSchema-row';
-        if (this.props.rowFocused) className += ' row-focused';
-        if (this.props.rowWrapped) className += ' row-wrapped';
+//         let className = 'collectionSchema-row';
+//         if (this.props.rowFocused) className += ' row-focused';
+//         if (this.props.rowWrapped) className += ' row-wrapped';
 
-        return (
-            <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
-                <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                    <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}>
-                        <div className="row-dragger">
-                            <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}>
-                                <FontAwesomeIcon icon="trash" size="sm" />
-                            </div>
-                            <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}>
-                                <FontAwesomeIcon icon="grip-vertical" size="sm" />
-                            </div>
-                            <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, OpenWhere.addRight)}>
-                                <FontAwesomeIcon icon="external-link-alt" size="sm" />
-                            </div>
-                        </div>
-                        {children}
-                    </ReactTableDefaults.TrComponent>
-                </div>
-            </div>
-        );
-    }
-}
+//         return (
+//             <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
+//                 <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+//                     <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}>
+//                         <div className="row-dragger">
+//                             <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}>
+//                                 <FontAwesomeIcon icon="trash" size="sm" />
+//                             </div>
+//                             <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}>
+//                                 <FontAwesomeIcon icon="grip-vertical" size="sm" />
+//                             </div>
+//                             <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, OpenWhere.addRight)}>
+//                                 <FontAwesomeIcon icon="external-link-alt" size="sm" />
+//                             </div>
+//                         </div>
+//                         {children}
+//                     </ReactTableDefaults.TrComponent>
+//                 </div>
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
index 260db4b88..f3f09cbf0 100644
--- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
@@ -1,649 +1,649 @@
-import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, untracked } from 'mobx';
-import { observer } from 'mobx-react';
-import Measure from 'react-measure';
-// import { Resize } from 'react-table';
-import { Doc, Opt } from '../../../../fields/Doc';
-import { List } from '../../../../fields/List';
-import { listSpec } from '../../../../fields/Schema';
-import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast, NumCast } from '../../../../fields/Types';
-import { TraceMobx } from '../../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-import { DocUtils } from '../../../documents/Documents';
-import { SelectionManager } from '../../../util/SelectionManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
-import { ContextMenu } from '../../ContextMenu';
-import { ContextMenuProps } from '../../ContextMenuItem';
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { DocumentView } from '../../nodes/DocumentView';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { CollectionSubView } from '../CollectionSubView';
-import './CollectionSchemaView.scss';
-// import { SchemaTable } from './SchemaTable';
-// bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
-
-export enum ColumnType {
-    Any,
-    Number,
-    String,
-    Boolean,
-    Doc,
-    Image,
-    List,
-    Date,
-}
-// this map should be used for keys that should have a const type of value
-const columnTypes: Map<string, ColumnType> = new Map([
-    ['title', ColumnType.String],
-    ['x', ColumnType.Number],
-    ['y', ColumnType.Number],
-    ['_width', ColumnType.Number],
-    ['_height', ColumnType.Number],
-    ['_nativeWidth', ColumnType.Number],
-    ['_nativeHeight', ColumnType.Number],
-    ['isPrototype', ColumnType.Boolean],
-    ['_curPage', ColumnType.Number],
-    ['_currentTimecode', ColumnType.Number],
-    ['zIndex', ColumnType.Number],
-]);
-
-@observer
-export class CollectionSchemaView extends CollectionSubView() {
-    private _previewCont?: HTMLDivElement;
-
-    @observable _previewDoc: Doc | undefined = undefined;
-    @observable _focusedTable: Doc = this.props.Document;
-    @observable _col: any = '';
-    @observable _menuWidth = 0;
-    @observable _headerOpen = false;
-    @observable _headerIsEditing = false;
-    @observable _menuHeight = 0;
-    @observable _pointerX = 0;
-    @observable _pointerY = 0;
-    @observable _openTypes: boolean = false;
-
-    @computed get previewWidth() {
-        return () => NumCast(this.props.Document.schemaPreviewWidth);
-    }
-    @computed get previewHeight() {
-        return () => this.props.PanelHeight() - 2 * this.borderWidth;
-    }
-    @computed get tableWidth() {
-        return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
-    }
-    @computed get borderWidth() {
-        return Number(COLLECTION_BORDER_WIDTH);
-    }
-    @computed get scale() {
-        return this.props.ScreenToLocalTransform().Scale;
-    }
-    @computed get columns() {
-        return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
-    }
-    set columns(columns: SchemaHeaderField[]) {
-        this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns);
-    }
-
-    @computed get menuCoordinates() {
-        let searchx = 0;
-        let searchy = 0;
-        if (this.props.Document._searchDoc) {
-            const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0];
-            if (el !== undefined) {
-                const rect = el.getBoundingClientRect();
-                searchx = rect.x;
-                searchy = rect.y;
-            }
-        }
-        const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx;
-        const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy;
-        return this.props.ScreenToLocalTransform().transformPoint(x, y);
-    }
-
-    get documentKeys() {
-        const docs = this.childDocs;
-        const keys: { [key: string]: boolean } = {};
-        // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
-        //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
-        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
-        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
-        //  is displayed (unlikely) it won't show up until something else changes.
-        //TODO Types
-        untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
-
-        this.columns.forEach(key => (keys[key.heading] = true));
-        return Array.from(Object.keys(keys));
-    }
-
-    @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing);
-
-    @undoBatch
-    setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => {
-        this._openTypes = false;
-        if (columnTypes.get(columnField.heading)) return;
-
-        const columns = this.columns;
-        const index = columns.indexOf(columnField);
-        if (index > -1) {
-            columnField.setType(NumCast(type));
-            columns[index] = columnField;
-            this.columns = columns;
-        }
-    });
-
-    @undoBatch
-    setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
-        const columns = this.columns;
-        const index = columns.indexOf(columnField);
-        if (index > -1) {
-            columnField.setColor(color);
-            columns[index] = columnField;
-            this.columns = columns; // need to set the columns to trigger rerender
-        }
-    };
-
-    @undoBatch
-    @action
-    setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
-        const columns = this.columns;
-        columns.forEach(col => col.setDesc(undefined));
-
-        const index = columns.findIndex(c => c.heading === columnField.heading);
-        const column = columns[index];
-        column.setDesc(descending);
-        columns[index] = column;
-        this.columns = columns;
-    };
-
-    renderTypes = (col: any) => {
-        if (columnTypes.get(col.heading)) return null;
-
-        const type = col.type;
-
-        const anyType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}>
-                <FontAwesomeIcon icon={'align-justify'} size="sm" />
-                Any
-            </div>
-        );
-
-        const numType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}>
-                <FontAwesomeIcon icon={'hashtag'} size="sm" />
-                Number
-            </div>
-        );
-
-        const textType = (
-            <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}>
-                <FontAwesomeIcon icon={'font'} size="sm" />
-                Text
-            </div>
-        );
-
-        const boolType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
-                <FontAwesomeIcon icon={'check-square'} size="sm" />
-                Checkbox
-            </div>
-        );
-
-        const listType = (
-            <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}>
-                <FontAwesomeIcon icon={'list-ul'} size="sm" />
-                List
-            </div>
-        );
-
-        const docType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
-                <FontAwesomeIcon icon={'file'} size="sm" />
-                Document
-            </div>
-        );
-
-        const imageType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}>
-                <FontAwesomeIcon icon={'image'} size="sm" />
-                Image
-            </div>
-        );
-
-        const dateType = (
-            <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}>
-                <FontAwesomeIcon icon={'calendar'} size="sm" />
-                Date
-            </div>
-        );
-
-        const allColumnTypes = (
-            <div className="columnMenu-types">
-                {anyType}
-                {numType}
-                {textType}
-                {boolType}
-                {listType}
-                {docType}
-                {imageType}
-                {dateType}
-            </div>
-        );
-
-        const justColType =
-            type === ColumnType.Any
-                ? anyType
-                : type === ColumnType.Number
-                ? numType
-                : type === ColumnType.String
-                ? textType
-                : type === ColumnType.Boolean
-                ? boolType
-                : type === ColumnType.List
-                ? listType
-                : type === ColumnType.Doc
-                ? docType
-                : type === ColumnType.Date
-                ? dateType
-                : imageType;
-
-        return (
-            <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}>
-                <div>
-                    <label style={{ cursor: 'pointer' }}>Column type:</label>
-                    <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} />
-                </div>
-                {this._openTypes ? allColumnTypes : justColType}
-            </div>
-        );
-    };
-
-    renderSorting = (col: any) => {
-        const sort = col.desc;
-        return (
-            <div className="collectionSchema-headerMenu-group">
-                <label>Sort by:</label>
-                <div className="columnMenu-sort">
-                    <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}>
-                        <FontAwesomeIcon icon="sort-amount-down" size="sm" />
-                        Sort descending
-                    </div>
-                    <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}>
-                        <FontAwesomeIcon icon="sort-amount-up" size="sm" />
-                        Sort ascending
-                    </div>
-                    <div className="columnMenu-option" onClick={() => this.setColumnSort(col, undefined)}>
-                        <FontAwesomeIcon icon="times" size="sm" />
-                        Clear sorting
-                    </div>
-                </div>
-            </div>
-        );
-    };
-
-    renderColors = (col: any) => {
-        const selected = col.color;
-
-        const pink = PastelSchemaPalette.get('pink2');
-        const purple = PastelSchemaPalette.get('purple2');
-        const blue = PastelSchemaPalette.get('bluegreen1');
-        const yellow = PastelSchemaPalette.get('yellow4');
-        const red = PastelSchemaPalette.get('red2');
-        const gray = '#f1efeb';
-
-        return (
-            <div className="collectionSchema-headerMenu-group">
-                <label>Color:</label>
-                <div className="columnMenu-colors">
-                    <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div>
-                    <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div>
-                </div>
-            </div>
-        );
-    };
-
-    @undoBatch
-    @action
-    changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => {
-        const columns = this.columns;
-        if (columns === undefined) {
-            this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]);
-        } else {
-            if (addNew) {
-                columns.push(new SchemaHeaderField(newKey, 'f1efeb'));
-                this.columns = columns;
-            } else {
-                const index = columns.map(c => c.heading).indexOf(oldKey);
-                if (index > -1) {
-                    const column = columns[index];
-                    column.setHeading(newKey);
-                    columns[index] = column;
-                    this.columns = columns;
-                    if (filter) {
-                        Doc.setDocFilter(this.props.Document, newKey, filter, 'match');
-                    } else {
-                        this.props.Document._docFilters = undefined;
-                    }
-                }
-            }
-        }
-    };
-
-    @action
-    openHeader = (col: any, screenx: number, screeny: number) => {
-        this._col = col;
-        this._headerOpen = true;
-        this._pointerX = screenx;
-        this._pointerY = screeny;
-    };
-
-    @action
-    closeHeader = () => {
-        this._headerOpen = false;
-    };
-
-    @undoBatch
-    @action
-    deleteColumn = (key: string) => {
-        const columns = this.columns;
-        if (columns === undefined) {
-            this.columns = new List<SchemaHeaderField>([]);
-        } else {
-            const index = columns.map(c => c.heading).indexOf(key);
-            if (index > -1) {
-                columns.splice(index, 1);
-                this.columns = columns;
-            }
-        }
-        this.closeHeader();
-    };
-
-    getPreviewTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth);
-    };
-
-    @action
-    onHeaderClick = (e: React.PointerEvent) => {
-        e.stopPropagation();
-    };
-
-    @action
-    onWheel(e: React.WheelEvent) {
-        const scale = this.props.ScreenToLocalTransform().Scale;
-        this.props.isContentActive(true) && e.stopPropagation();
-    }
-
-    @computed get renderMenuContent() {
-        TraceMobx();
-        return (
-            <div className="collectionSchema-header-menuOptions">
-                {this.renderTypes(this._col)}
-                {this.renderColors(this._col)}
-                <div className="collectionSchema-headerMenu-group">
-                    <button
-                        onClick={() => {
-                            this.deleteColumn(this._col.heading);
-                        }}>
-                        Hide Column
-                    </button>
-                </div>
-            </div>
-        );
-    }
-
-    private createTarget = (ele: HTMLDivElement) => {
-        this._previewCont = ele;
-        super.CreateDropTarget(ele);
-    };
-
-    isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
-
-    @action setFocused = (doc: Doc) => (this._focusedTable = doc);
-
-    @action setPreviewDoc = (doc: Opt<Doc>) => {
-        SelectionManager.SelectSchemaViewDoc(doc);
-        this._previewDoc = doc;
-    };
-
-    //toggles preview side-panel of schema
-    @action
-    toggleExpander = () => {
-        this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0;
-    };
-
-    onDividerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander);
-    };
-    @action
-    onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
-        const nativeWidth = this._previewCont!.getBoundingClientRect();
-        const minWidth = 40;
-        const maxWidth = 1000;
-        const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
-        const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
-        this.props.Document.schemaPreviewWidth = width;
-        return false;
-    };
-
-    onPointerDown = (e: React.PointerEvent): void => {
-        if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
-            if (this.props.isSelected(true)) e.stopPropagation();
-            else this.props.select(false);
-        }
-    };
-
-    @computed
-    get previewDocument(): Doc | undefined {
-        return this._previewDoc;
-    }
-
-    @computed
-    get dividerDragger() {
-        return this.previewWidth() === 0 ? null : (
-            <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}>
-                <div className="collectionSchemaView-dividerDragger" />
-            </div>
-        );
-    }
-
-    @computed
-    get previewPanel() {
-        return (
-            <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}>
-                {!this.previewDocument ? null : (
-                    <DocumentView
-                        Document={this.previewDocument}
-                        DataDoc={undefined}
-                        fitContentsToBox={returnTrue}
-                        dontCenter={'y'}
-                        focus={DocUtils.DefaultFocus}
-                        renderDepth={this.props.renderDepth}
-                        rootSelected={this.rootSelected}
-                        PanelWidth={this.previewWidth}
-                        PanelHeight={this.previewHeight}
-                        isContentActive={returnTrue}
-                        isDocumentActive={returnFalse}
-                        ScreenToLocalTransform={this.getPreviewTransform}
-                        docFilters={this.childDocFilters}
-                        docRangeFilters={this.childDocRangeFilters}
-                        searchFilterDocs={this.searchFilterDocs}
-                        styleProvider={DefaultStyleProvider}
-                        docViewPath={returnEmptyDoclist}
-                        ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                        ContainingCollectionView={this.props.CollectionView}
-                        moveDocument={this.props.moveDocument}
-                        addDocument={this.props.addDocument}
-                        removeDocument={this.props.removeDocument}
-                        whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-                        addDocTab={this.props.addDocTab}
-                        pinToPres={this.props.pinToPres}
-                        bringToFront={returnFalse}
-                    />
-                )}
-            </div>
-        );
-    }
-
-    @computed
-    get schemaTable() {
-        return (
-            <SchemaTable
-                Document={this.props.Document}
-                PanelHeight={this.props.PanelHeight}
-                PanelWidth={this.props.PanelWidth}
-                childDocs={this.childDocs}
-                CollectionView={this.props.CollectionView}
-                ContainingCollectionView={this.props.ContainingCollectionView}
-                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                fieldKey={this.props.fieldKey}
-                renderDepth={this.props.renderDepth}
-                moveDocument={this.props.moveDocument}
-                ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                active={this.props.isContentActive}
-                onDrop={this.onExternalDrop}
-                addDocTab={this.props.addDocTab}
-                pinToPres={this.props.pinToPres}
-                isSelected={this.props.isSelected}
-                isFocused={this.isFocused}
-                setFocused={this.setFocused}
-                setPreviewDoc={this.setPreviewDoc}
-                deleteDocument={this.props.removeDocument}
-                addDocument={this.props.addDocument}
-                dataDoc={this.props.DataDoc}
-                columns={this.columns}
-                documentKeys={this.documentKeys}
-                headerIsEditing={this._headerIsEditing}
-                openHeader={this.openHeader}
-                onClick={this.onTableClick}
-                onPointerDown={emptyFunction}
-                onResizedChange={this.onResizedChange}
-                setColumns={this.setColumns}
-                reorderColumns={this.reorderColumns}
-                changeColumns={this.changeColumns}
-                setHeaderIsEditing={this.setHeaderIsEditing}
-                changeColumnSort={this.setColumnSort}
-            />
-        );
-    }
-
-    @computed
-    public get schemaToolbar() {
-        return (
-            <div className="collectionSchemaView-toolbar">
-                <div className="collectionSchemaView-toolbar-item">
-                    <div id="preview-schema-checkbox-div">
-                        <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />
-                        Show Preview
-                    </div>
-                </div>
-            </div>
-        );
-    }
-
-    onSpecificMenu = (e: React.MouseEvent) => {
-        if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) {
-            const cm = ContextMenu.Instance;
-            const options = cm.findByDescription('Options...');
-            const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
-            optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' });
-            !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
-            cm.displayMenu(e.clientX, e.clientY);
-            (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
-            e.stopPropagation();
-        }
-    };
-
-    @action
-    onTableClick = (e: React.MouseEvent): void => {
-        if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) {
-            this.setPreviewDoc(undefined);
-        } else {
-            e.stopPropagation();
-        }
-        this.setFocused(this.props.Document);
-        this.closeHeader();
-    };
-
-    onResizedChange = (newResized: Resize[], event: any) => {
-        const columns = this.columns;
-        newResized.forEach(resized => {
-            const index = columns.findIndex(c => c.heading === resized.id);
-            const column = columns[index];
-            column.setWidth(resized.value);
-            columns[index] = column;
-        });
-        this.columns = columns;
-    };
-
-    @action
-    setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns);
-
-    @undoBatch
-    reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
-        const columns = [...columnsValues];
-        const oldIndex = columns.indexOf(toMove);
-        const relIndex = columns.indexOf(relativeTo);
-        const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex;
-
-        if (oldIndex === newIndex) return;
-
-        columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
-        this.columns = columns;
-    };
-
-    onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation();
-
-    render() {
-        TraceMobx();
-        if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0);
-        const menuContent = this.renderMenuContent;
-        const menu = (
-            <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}>
-                <Measure
-                    offset
-                    onResize={action((r: any) => {
-                        const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
-                        this._menuWidth = dim[0];
-                        this._menuHeight = dim[1];
-                    })}>
-                    {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
-                </Measure>
-            </div>
-        );
-        return (
-            <div
-                className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')}
-                style={{
-                    overflow: this.props.scrollOverflow === true ? 'scroll' : undefined,
-                    backgroundColor: 'white',
-                    pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
-                    width: this.props.PanelWidth() || '100%',
-                    height: this.props.PanelHeight() || '100%',
-                    position: 'relative',
-                }}>
-                <div
-                    className="collectionSchemaView-tableContainer"
-                    style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
-                    onContextMenu={this.onSpecificMenu}
-                    onPointerDown={this.onPointerDown}
-                    onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}
-                    onDrop={e => this.onExternalDrop(e, {})}
-                    ref={this.createTarget}>
-                    {this.schemaTable}
-                </div>
-                {this.dividerDragger}
-                {!this.previewWidth() ? null : this.previewPanel}
-                {this._headerOpen && this.props.isContentActive() ? menu : null}
-            </div>
-        );
-        TraceMobx();
-        return <div>HELLO</div>;
-    }
-}
+// import React = require('react');
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action, computed, observable, untracked } from 'mobx';
+// import { observer } from 'mobx-react';
+// import Measure from 'react-measure';
+// // import { Resize } from 'react-table';
+// import { Doc, Opt } from '../../../../fields/Doc';
+// import { List } from '../../../../fields/List';
+// import { listSpec } from '../../../../fields/Schema';
+// import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { Cast, NumCast } from '../../../../fields/Types';
+// import { TraceMobx } from '../../../../fields/util';
+// import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+// import { DocUtils } from '../../../documents/Documents';
+// import { SelectionManager } from '../../../util/SelectionManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { Transform } from '../../../util/Transform';
+// import { undoBatch } from '../../../util/UndoManager';
+// import { ContextMenu } from '../../ContextMenu';
+// import { ContextMenuProps } from '../../ContextMenuItem';
+// import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
+// import { DocumentView } from '../../nodes/DocumentView';
+// import { DefaultStyleProvider } from '../../StyleProvider';
+// import { CollectionSubView } from '../CollectionSubView';
+// import './CollectionSchemaView.scss';
+// // import { SchemaTable } from './SchemaTable';
+// // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
+
+// export enum ColumnType {
+//     Any,
+//     Number,
+//     String,
+//     Boolean,
+//     Doc,
+//     Image,
+//     List,
+//     Date,
+// }
+// // this map should be used for keys that should have a const type of value
+// const columnTypes: Map<string, ColumnType> = new Map([
+//     ['title', ColumnType.String],
+//     ['x', ColumnType.Number],
+//     ['y', ColumnType.Number],
+//     ['_width', ColumnType.Number],
+//     ['_height', ColumnType.Number],
+//     ['_nativeWidth', ColumnType.Number],
+//     ['_nativeHeight', ColumnType.Number],
+//     ['isPrototype', ColumnType.Boolean],
+//     ['_curPage', ColumnType.Number],
+//     ['_currentTimecode', ColumnType.Number],
+//     ['zIndex', ColumnType.Number],
+// ]);
+
+// @observer
+// export class CollectionSchemaView extends CollectionSubView() {
+//     private _previewCont?: HTMLDivElement;
+
+//     @observable _previewDoc: Doc | undefined = undefined;
+//     @observable _focusedTable: Doc = this.props.Document;
+//     @observable _col: any = '';
+//     @observable _menuWidth = 0;
+//     @observable _headerOpen = false;
+//     @observable _headerIsEditing = false;
+//     @observable _menuHeight = 0;
+//     @observable _pointerX = 0;
+//     @observable _pointerY = 0;
+//     @observable _openTypes: boolean = false;
+
+//     @computed get previewWidth() {
+//         return () => NumCast(this.props.Document.schemaPreviewWidth);
+//     }
+//     @computed get previewHeight() {
+//         return () => this.props.PanelHeight() - 2 * this.borderWidth;
+//     }
+//     @computed get tableWidth() {
+//         return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
+//     }
+//     @computed get borderWidth() {
+//         return Number(COLLECTION_BORDER_WIDTH);
+//     }
+//     @computed get scale() {
+//         return this.props.ScreenToLocalTransform().Scale;
+//     }
+//     @computed get columns() {
+//         return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
+//     }
+//     set columns(columns: SchemaHeaderField[]) {
+//         this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns);
+//     }
+
+//     @computed get menuCoordinates() {
+//         let searchx = 0;
+//         let searchy = 0;
+//         if (this.props.Document._searchDoc) {
+//             const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0];
+//             if (el !== undefined) {
+//                 const rect = el.getBoundingClientRect();
+//                 searchx = rect.x;
+//                 searchy = rect.y;
+//             }
+//         }
+//         const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx;
+//         const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy;
+//         return this.props.ScreenToLocalTransform().transformPoint(x, y);
+//     }
+
+//     get documentKeys() {
+//         const docs = this.childDocs;
+//         const keys: { [key: string]: boolean } = {};
+//         // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
+//         //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
+//         //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
+//         //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
+//         //  is displayed (unlikely) it won't show up until something else changes.
+//         //TODO Types
+//         untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
+
+//         this.columns.forEach(key => (keys[key.heading] = true));
+//         return Array.from(Object.keys(keys));
+//     }
+
+//     @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing);
+
+//     @undoBatch
+//     setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => {
+//         this._openTypes = false;
+//         if (columnTypes.get(columnField.heading)) return;
+
+//         const columns = this.columns;
+//         const index = columns.indexOf(columnField);
+//         if (index > -1) {
+//             columnField.setType(NumCast(type));
+//             columns[index] = columnField;
+//             this.columns = columns;
+//         }
+//     });
+
+//     @undoBatch
+//     setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
+//         const columns = this.columns;
+//         const index = columns.indexOf(columnField);
+//         if (index > -1) {
+//             columnField.setColor(color);
+//             columns[index] = columnField;
+//             this.columns = columns; // need to set the columns to trigger rerender
+//         }
+//     };
+
+//     @undoBatch
+//     @action
+//     setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
+//         const columns = this.columns;
+//         columns.forEach(col => col.setDesc(undefined));
+
+//         const index = columns.findIndex(c => c.heading === columnField.heading);
+//         const column = columns[index];
+//         column.setDesc(descending);
+//         columns[index] = column;
+//         this.columns = columns;
+//     };
+
+//     renderTypes = (col: any) => {
+//         if (columnTypes.get(col.heading)) return null;
+
+//         const type = col.type;
+
+//         const anyType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}>
+//                 <FontAwesomeIcon icon={'align-justify'} size="sm" />
+//                 Any
+//             </div>
+//         );
+
+//         const numType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}>
+//                 <FontAwesomeIcon icon={'hashtag'} size="sm" />
+//                 Number
+//             </div>
+//         );
+
+//         const textType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}>
+//                 <FontAwesomeIcon icon={'font'} size="sm" />
+//                 Text
+//             </div>
+//         );
+
+//         const boolType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
+//                 <FontAwesomeIcon icon={'check-square'} size="sm" />
+//                 Checkbox
+//             </div>
+//         );
+
+//         const listType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}>
+//                 <FontAwesomeIcon icon={'list-ul'} size="sm" />
+//                 List
+//             </div>
+//         );
+
+//         const docType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
+//                 <FontAwesomeIcon icon={'file'} size="sm" />
+//                 Document
+//             </div>
+//         );
+
+//         const imageType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}>
+//                 <FontAwesomeIcon icon={'image'} size="sm" />
+//                 Image
+//             </div>
+//         );
+
+//         const dateType = (
+//             <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}>
+//                 <FontAwesomeIcon icon={'calendar'} size="sm" />
+//                 Date
+//             </div>
+//         );
+
+//         const allColumnTypes = (
+//             <div className="columnMenu-types">
+//                 {anyType}
+//                 {numType}
+//                 {textType}
+//                 {boolType}
+//                 {listType}
+//                 {docType}
+//                 {imageType}
+//                 {dateType}
+//             </div>
+//         );
+
+//         const justColType =
+//             type === ColumnType.Any
+//                 ? anyType
+//                 : type === ColumnType.Number
+//                 ? numType
+//                 : type === ColumnType.String
+//                 ? textType
+//                 : type === ColumnType.Boolean
+//                 ? boolType
+//                 : type === ColumnType.List
+//                 ? listType
+//                 : type === ColumnType.Doc
+//                 ? docType
+//                 : type === ColumnType.Date
+//                 ? dateType
+//                 : imageType;
+
+//         return (
+//             <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}>
+//                 <div>
+//                     <label style={{ cursor: 'pointer' }}>Column type:</label>
+//                     <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} />
+//                 </div>
+//                 {this._openTypes ? allColumnTypes : justColType}
+//             </div>
+//         );
+//     };
+
+//     renderSorting = (col: any) => {
+//         const sort = col.desc;
+//         return (
+//             <div className="collectionSchema-headerMenu-group">
+//                 <label>Sort by:</label>
+//                 <div className="columnMenu-sort">
+//                     <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}>
+//                         <FontAwesomeIcon icon="sort-amount-down" size="sm" />
+//                         Sort descending
+//                     </div>
+//                     <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}>
+//                         <FontAwesomeIcon icon="sort-amount-up" size="sm" />
+//                         Sort ascending
+//                     </div>
+//                     <div className="columnMenu-option" onClick={() => this.setColumnSort(col, undefined)}>
+//                         <FontAwesomeIcon icon="times" size="sm" />
+//                         Clear sorting
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     };
+
+//     renderColors = (col: any) => {
+//         const selected = col.color;
+
+//         const pink = PastelSchemaPalette.get('pink2');
+//         const purple = PastelSchemaPalette.get('purple2');
+//         const blue = PastelSchemaPalette.get('bluegreen1');
+//         const yellow = PastelSchemaPalette.get('yellow4');
+//         const red = PastelSchemaPalette.get('red2');
+//         const gray = '#f1efeb';
+
+//         return (
+//             <div className="collectionSchema-headerMenu-group">
+//                 <label>Color:</label>
+//                 <div className="columnMenu-colors">
+//                     <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div>
+//                     <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div>
+//                 </div>
+//             </div>
+//         );
+//     };
+
+//     @undoBatch
+//     @action
+//     changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => {
+//         const columns = this.columns;
+//         if (columns === undefined) {
+//             this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]);
+//         } else {
+//             if (addNew) {
+//                 columns.push(new SchemaHeaderField(newKey, 'f1efeb'));
+//                 this.columns = columns;
+//             } else {
+//                 const index = columns.map(c => c.heading).indexOf(oldKey);
+//                 if (index > -1) {
+//                     const column = columns[index];
+//                     column.setHeading(newKey);
+//                     columns[index] = column;
+//                     this.columns = columns;
+//                     if (filter) {
+//                         Doc.setDocFilter(this.props.Document, newKey, filter, 'match');
+//                     } else {
+//                         this.props.Document._docFilters = undefined;
+//                     }
+//                 }
+//             }
+//         }
+//     };
+
+//     @action
+//     openHeader = (col: any, screenx: number, screeny: number) => {
+//         this._col = col;
+//         this._headerOpen = true;
+//         this._pointerX = screenx;
+//         this._pointerY = screeny;
+//     };
+
+//     @action
+//     closeHeader = () => {
+//         this._headerOpen = false;
+//     };
+
+//     @undoBatch
+//     @action
+//     deleteColumn = (key: string) => {
+//         const columns = this.columns;
+//         if (columns === undefined) {
+//             this.columns = new List<SchemaHeaderField>([]);
+//         } else {
+//             const index = columns.map(c => c.heading).indexOf(key);
+//             if (index > -1) {
+//                 columns.splice(index, 1);
+//                 this.columns = columns;
+//             }
+//         }
+//         this.closeHeader();
+//     };
+
+//     getPreviewTransform = (): Transform => {
+//         return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth);
+//     };
+
+//     @action
+//     onHeaderClick = (e: React.PointerEvent) => {
+//         e.stopPropagation();
+//     };
+
+//     @action
+//     onWheel(e: React.WheelEvent) {
+//         const scale = this.props.ScreenToLocalTransform().Scale;
+//         this.props.isContentActive(true) && e.stopPropagation();
+//     }
+
+//     @computed get renderMenuContent() {
+//         TraceMobx();
+//         return (
+//             <div className="collectionSchema-header-menuOptions">
+//                 {this.renderTypes(this._col)}
+//                 {this.renderColors(this._col)}
+//                 <div className="collectionSchema-headerMenu-group">
+//                     <button
+//                         onClick={() => {
+//                             this.deleteColumn(this._col.heading);
+//                         }}>
+//                         Hide Column
+//                     </button>
+//                 </div>
+//             </div>
+//         );
+//     }
+
+//     private createTarget = (ele: HTMLDivElement) => {
+//         this._previewCont = ele;
+//         super.CreateDropTarget(ele);
+//     };
+
+//     isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
+
+//     @action setFocused = (doc: Doc) => (this._focusedTable = doc);
+
+//     @action setPreviewDoc = (doc: Opt<Doc>) => {
+//         SelectionManager.SelectSchemaViewDoc(doc);
+//         this._previewDoc = doc;
+//     };
+
+//     //toggles preview side-panel of schema
+//     @action
+//     toggleExpander = () => {
+//         this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0;
+//     };
+
+//     onDividerDown = (e: React.PointerEvent) => {
+//         setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander);
+//     };
+//     @action
+//     onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+//         const nativeWidth = this._previewCont!.getBoundingClientRect();
+//         const minWidth = 40;
+//         const maxWidth = 1000;
+//         const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
+//         const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
+//         this.props.Document.schemaPreviewWidth = width;
+//         return false;
+//     };
+
+//     onPointerDown = (e: React.PointerEvent): void => {
+//         if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
+//             if (this.props.isSelected(true)) e.stopPropagation();
+//             else this.props.select(false);
+//         }
+//     };
+
+//     @computed
+//     get previewDocument(): Doc | undefined {
+//         return this._previewDoc;
+//     }
+
+//     @computed
+//     get dividerDragger() {
+//         return this.previewWidth() === 0 ? null : (
+//             <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}>
+//                 <div className="collectionSchemaView-dividerDragger" />
+//             </div>
+//         );
+//     }
+
+//     @computed
+//     get previewPanel() {
+//         return (
+//             <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}>
+//                 {!this.previewDocument ? null : (
+//                     <DocumentView
+//                         Document={this.previewDocument}
+//                         DataDoc={undefined}
+//                         fitContentsToBox={returnTrue}
+//                         dontCenter={'y'}
+//                         focus={DocUtils.DefaultFocus}
+//                         renderDepth={this.props.renderDepth}
+//                         rootSelected={this.rootSelected}
+//                         PanelWidth={this.previewWidth}
+//                         PanelHeight={this.previewHeight}
+//                         isContentActive={returnTrue}
+//                         isDocumentActive={returnFalse}
+//                         ScreenToLocalTransform={this.getPreviewTransform}
+//                         docFilters={this.childDocFilters}
+//                         docRangeFilters={this.childDocRangeFilters}
+//                         searchFilterDocs={this.searchFilterDocs}
+//                         styleProvider={DefaultStyleProvider}
+//                         docViewPath={returnEmptyDoclist}
+//                         ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+//                         ContainingCollectionView={this.props.CollectionView}
+//                         moveDocument={this.props.moveDocument}
+//                         addDocument={this.props.addDocument}
+//                         removeDocument={this.props.removeDocument}
+//                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+//                         addDocTab={this.props.addDocTab}
+//                         pinToPres={this.props.pinToPres}
+//                         bringToFront={returnFalse}
+//                     />
+//                 )}
+//             </div>
+//         );
+//     }
+
+//     @computed
+//     get schemaTable() {
+//         return (
+//             <SchemaTable
+//                 Document={this.props.Document}
+//                 PanelHeight={this.props.PanelHeight}
+//                 PanelWidth={this.props.PanelWidth}
+//                 childDocs={this.childDocs}
+//                 CollectionView={this.props.CollectionView}
+//                 ContainingCollectionView={this.props.ContainingCollectionView}
+//                 ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+//                 fieldKey={this.props.fieldKey}
+//                 renderDepth={this.props.renderDepth}
+//                 moveDocument={this.props.moveDocument}
+//                 ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+//                 active={this.props.isContentActive}
+//                 onDrop={this.onExternalDrop}
+//                 addDocTab={this.props.addDocTab}
+//                 pinToPres={this.props.pinToPres}
+//                 isSelected={this.props.isSelected}
+//                 isFocused={this.isFocused}
+//                 setFocused={this.setFocused}
+//                 setPreviewDoc={this.setPreviewDoc}
+//                 deleteDocument={this.props.removeDocument}
+//                 addDocument={this.props.addDocument}
+//                 dataDoc={this.props.DataDoc}
+//                 columns={this.columns}
+//                 documentKeys={this.documentKeys}
+//                 headerIsEditing={this._headerIsEditing}
+//                 openHeader={this.openHeader}
+//                 onClick={this.onTableClick}
+//                 onPointerDown={emptyFunction}
+//                 onResizedChange={this.onResizedChange}
+//                 setColumns={this.setColumns}
+//                 reorderColumns={this.reorderColumns}
+//                 changeColumns={this.changeColumns}
+//                 setHeaderIsEditing={this.setHeaderIsEditing}
+//                 changeColumnSort={this.setColumnSort}
+//             />
+//         );
+//     }
+
+//     @computed
+//     public get schemaToolbar() {
+//         return (
+//             <div className="collectionSchemaView-toolbar">
+//                 <div className="collectionSchemaView-toolbar-item">
+//                     <div id="preview-schema-checkbox-div">
+//                         <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />
+//                         Show Preview
+//                     </div>
+//                 </div>
+//             </div>
+//         );
+//     }
+
+//     onSpecificMenu = (e: React.MouseEvent) => {
+//         if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) {
+//             const cm = ContextMenu.Instance;
+//             const options = cm.findByDescription('Options...');
+//             const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+//             optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' });
+//             !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
+//             cm.displayMenu(e.clientX, e.clientY);
+//             (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
+//             e.stopPropagation();
+//         }
+//     };
+
+//     @action
+//     onTableClick = (e: React.MouseEvent): void => {
+//         if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) {
+//             this.setPreviewDoc(undefined);
+//         } else {
+//             e.stopPropagation();
+//         }
+//         this.setFocused(this.props.Document);
+//         this.closeHeader();
+//     };
+
+//     onResizedChange = (newResized: Resize[], event: any) => {
+//         const columns = this.columns;
+//         newResized.forEach(resized => {
+//             const index = columns.findIndex(c => c.heading === resized.id);
+//             const column = columns[index];
+//             column.setWidth(resized.value);
+//             columns[index] = column;
+//         });
+//         this.columns = columns;
+//     };
+
+//     @action
+//     setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns);
+
+//     @undoBatch
+//     reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
+//         const columns = [...columnsValues];
+//         const oldIndex = columns.indexOf(toMove);
+//         const relIndex = columns.indexOf(relativeTo);
+//         const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex;
+
+//         if (oldIndex === newIndex) return;
+
+//         columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
+//         this.columns = columns;
+//     };
+
+//     onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation();
+
+//     render() {
+//         TraceMobx();
+//         if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0);
+//         const menuContent = this.renderMenuContent;
+//         const menu = (
+//             <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}>
+//                 <Measure
+//                     offset
+//                     onResize={action((r: any) => {
+//                         const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
+//                         this._menuWidth = dim[0];
+//                         this._menuHeight = dim[1];
+//                     })}>
+//                     {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
+//                 </Measure>
+//             </div>
+//         );
+//         return (
+//             <div
+//                 className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')}
+//                 style={{
+//                     overflow: this.props.scrollOverflow === true ? 'scroll' : undefined,
+//                     backgroundColor: 'white',
+//                     pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+//                     width: this.props.PanelWidth() || '100%',
+//                     height: this.props.PanelHeight() || '100%',
+//                     position: 'relative',
+//                 }}>
+//                 <div
+//                     className="collectionSchemaView-tableContainer"
+//                     style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
+//                     onContextMenu={this.onSpecificMenu}
+//                     onPointerDown={this.onPointerDown}
+//                     onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}
+//                     onDrop={e => this.onExternalDrop(e, {})}
+//                     ref={this.createTarget}>
+//                     {this.schemaTable}
+//                 </div>
+//                 {this.dividerDragger}
+//                 {!this.previewWidth() ? null : this.previewPanel}
+//                 {this._headerOpen && this.props.isContentActive() ? menu : null}
+//             </div>
+//         );
+//         TraceMobx();
+//         return <div>HELLO</div>;
+//     }
+// }
diff --git a/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
index 1b4fcf0a4..bc33f3be5 100644
--- a/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
@@ -1,694 +1,694 @@
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table';
-import { DateField } from '../../../../fields/DateField';
-import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { List } from '../../../../fields/List';
-import { listSpec } from '../../../../fields/Schema';
-import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { ComputedField } from '../../../../fields/ScriptField';
-import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
-import { ImageField } from '../../../../fields/URLField';
-import { GetEffectiveAcl } from '../../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils';
-import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CompileScript, Transformer, ts } from '../../../util/Scripting';
-import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
-import '../../../views/DocumentDecorations.scss';
-import { ContextMenu } from '../../ContextMenu';
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
-import { PinProps } from '../../nodes/trails';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { CollectionView } from '../CollectionView';
-import {
-    CellProps,
-    CollectionSchemaButtons,
-    CollectionSchemaCell,
-    CollectionSchemaCheckboxCell,
-    CollectionSchemaDateCell,
-    CollectionSchemaDocCell,
-    CollectionSchemaImageCell,
-    CollectionSchemaListCell,
-    CollectionSchemaNumberCell,
-    CollectionSchemaStringCell,
-} from './CollectionSchemaCells';
-import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders';
-import { MovableColumn } from './OldCollectionSchemaMovableColumn';
-import { MovableRow } from './CollectionSchemaMovableRow';
-import './CollectionSchemaView.scss';
-
-enum ColumnType {
-    Any,
-    Number,
-    String,
-    Boolean,
-    Doc,
-    Image,
-    List,
-    Date,
-}
-
-// this map should be used for keys that should have a const type of value
-const columnTypes: Map<string, ColumnType> = new Map([
-    ['title', ColumnType.String],
-    ['x', ColumnType.Number],
-    ['y', ColumnType.Number],
-    ['_width', ColumnType.Number],
-    ['_height', ColumnType.Number],
-    ['_nativeWidth', ColumnType.Number],
-    ['_nativeHeight', ColumnType.Number],
-    ['isPrototype', ColumnType.Boolean],
-    ['_curPage', ColumnType.Number],
-    ['_currentTimecode', ColumnType.Number],
-    ['zIndex', ColumnType.Number],
-]);
-
-export interface SchemaTableProps {
-    Document: Doc; // child doc
-    dataDoc?: Doc;
-    PanelHeight: () => number;
-    PanelWidth: () => number;
-    childDocs?: Doc[];
-    CollectionView: Opt<CollectionView>;
-    ContainingCollectionView: Opt<CollectionView>;
-    ContainingCollectionDoc: Opt<Doc>;
-    fieldKey: string;
-    renderDepth: number;
-    deleteDocument?: (document: Doc | Doc[]) => boolean;
-    addDocument?: (document: Doc | Doc[]) => boolean;
-    moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    ScreenToLocalTransform: () => Transform;
-    active: (outsideReaction: boolean | undefined) => boolean | undefined;
-    onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
-    addDocTab: (document: Doc, where: OpenWhere) => boolean;
-    pinToPres: (document: Doc, pinProps: PinProps) => void;
-    isSelected: (outsideReaction?: boolean) => boolean;
-    isFocused: (document: Doc, outsideReaction: boolean) => boolean;
-    setFocused: (document: Doc) => void;
-    setPreviewDoc: (document: Opt<Doc>) => void;
-    columns: SchemaHeaderField[];
-    documentKeys: any[];
-    headerIsEditing: boolean;
-    openHeader: (column: any, screenx: number, screeny: number) => void;
-    onClick: (e: React.MouseEvent) => void;
-    onPointerDown: (e: React.PointerEvent) => void;
-    onResizedChange: (newResized: Resize[], event: any) => void;
-    setColumns: (columns: SchemaHeaderField[]) => void;
-    reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => void;
-    changeColumns: (oldKey: string, newKey: string, addNew: boolean) => void;
-    setHeaderIsEditing: (isEditing: boolean) => void;
-    changeColumnSort: (columnField: SchemaHeaderField, descending: boolean | undefined) => void;
-}
-
-@observer
-export class SchemaTable extends React.Component<SchemaTableProps> {
-    @observable _cellIsEditing: boolean = false;
-    @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 };
-    @observable _openCollections: Set<number> = new Set();
-
-    @observable _showDoc: Doc | undefined;
-    @observable _showDataDoc: any = '';
-    @observable _showDocPos: number[] = [];
-
-    @observable _showTitleDropdown: boolean = false;
-
-    @computed get previewWidth() {
-        return () => NumCast(this.props.Document.schemaPreviewWidth);
-    }
-    @computed get previewHeight() {
-        return () => this.props.PanelHeight() - 2 * this.borderWidth;
-    }
-    @computed get tableWidth() {
-        return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
-    }
-
-    @computed get childDocs() {
-        if (this.props.childDocs) return this.props.childDocs;
-
-        const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
-        return DocListCast(doc[this.props.fieldKey]);
-    }
-    set childDocs(docs: Doc[]) {
-        const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
-        doc[this.props.fieldKey] = new List<Doc>(docs);
-    }
-
-    @computed get textWrappedRows() {
-        return Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
-    }
-    set textWrappedRows(textWrappedRows: string[]) {
-        this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows);
-    }
-
-    @computed get resized(): { id: string; value: number }[] {
-        return this.props.columns.reduce((resized, shf) => {
-            shf.width > -1 && resized.push({ id: shf.heading, value: shf.width });
-            return resized;
-        }, [] as { id: string; value: number }[]);
-    }
-    @computed get sorted(): SortingRule[] {
-        return this.props.columns.reduce((sorted, shf) => {
-            shf.desc !== undefined && sorted.push({ id: shf.heading, desc: shf.desc });
-            return sorted;
-        }, [] as SortingRule[]);
-    }
-
-    @action
-    changeSorting = (col: any) => {
-        this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
-    };
-
-    @action
-    changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown);
-
-    @computed get borderWidth() {
-        return Number(COLLECTION_BORDER_WIDTH);
-    }
-    @computed get tableColumns(): Column<Doc>[] {
-        const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
-        const columns: Column<Doc>[] = [];
-        const tableIsFocused = this.props.isFocused(this.props.Document, false);
-        const focusedRow = this._focusedCell.row;
-        const focusedCol = this._focusedCell.col;
-        const isEditable = !this.props.headerIsEditing;
-
-        columns.push({
-            expander: true,
-            Header: '',
-            width: 58,
-            Expander: rowInfo => {
-                return rowInfo.original.type !== DocumentType.COL ? null : (
-                    <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}>
-                        <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" />
-                    </div>
-                );
-            },
-        });
-        columns.push(
-            ...this.props.columns.map(col => {
-                const icon: IconProp =
-                    this.getColumnType(col) === ColumnType.Number
-                        ? 'hashtag'
-                        : this.getColumnType(col) === ColumnType.String
-                        ? 'font'
-                        : this.getColumnType(col) === ColumnType.Boolean
-                        ? 'check-square'
-                        : this.getColumnType(col) === ColumnType.Doc
-                        ? 'file'
-                        : this.getColumnType(col) === ColumnType.Image
-                        ? 'image'
-                        : this.getColumnType(col) === ColumnType.List
-                        ? 'list-ul'
-                        : this.getColumnType(col) === ColumnType.Date
-                        ? 'calendar'
-                        : 'align-justify';
-
-                const keysDropdown = (
-                    <KeysDropdown
-                        keyValue={col.heading}
-                        possibleKeys={possibleKeys}
-                        existingKeys={this.props.columns.map(c => c.heading)}
-                        canAddNew={true}
-                        addNew={false}
-                        onSelect={this.props.changeColumns}
-                        setIsEditing={this.props.setHeaderIsEditing}
-                        docs={this.props.childDocs}
-                        Document={this.props.Document}
-                        dataDoc={this.props.dataDoc}
-                        fieldKey={this.props.fieldKey}
-                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                        ContainingCollectionView={this.props.ContainingCollectionView}
-                        active={this.props.active}
-                        openHeader={this.props.openHeader}
-                        icon={icon}
-                        col={col}
-                        // try commenting this out
-                        width={'100%'}
-                    />
-                );
-
-                const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up';
-                const header = (
-                    <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}>
-                        {keysDropdown}
-                        <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}>
-                            <FontAwesomeIcon icon={sortIcon} size="lg" />
-                        </div>
-                        {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
-                    </div>
-                );
-
-                return {
-                    Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
-                    accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0),
-                    id: col.heading,
-                    Cell: (rowProps: CellInfo) => {
-                        const rowIndex = rowProps.index;
-                        const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
-                        const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
-
-                        const props: CellProps = {
-                            row: rowIndex,
-                            col: columnIndex,
-                            rowProps: rowProps,
-                            isFocused: isFocused,
-                            changeFocusedCellByIndex: this.changeFocusedCellByIndex,
-                            CollectionView: this.props.CollectionView,
-                            ContainingCollection: this.props.ContainingCollectionView,
-                            Document: this.props.Document,
-                            fieldKey: this.props.fieldKey,
-                            renderDepth: this.props.renderDepth,
-                            addDocTab: this.props.addDocTab,
-                            pinToPres: this.props.pinToPres,
-                            moveDocument: this.props.moveDocument,
-                            setIsEditing: this.setCellIsEditing,
-                            isEditable: isEditable,
-                            setPreviewDoc: this.props.setPreviewDoc,
-                            setComputed: this.setComputed,
-                            getField: this.getField,
-                            showDoc: this.showDoc,
-                        };
-
-                        switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
-                            case ColumnType.Number:
-                                return <CollectionSchemaNumberCell {...props} />;
-                            case ColumnType.String:
-                                return <CollectionSchemaStringCell {...props} />;
-                            case ColumnType.Boolean:
-                                return <CollectionSchemaCheckboxCell {...props} />;
-                            case ColumnType.Doc:
-                                return <CollectionSchemaDocCell {...props} />;
-                            case ColumnType.Image:
-                                return <CollectionSchemaImageCell {...props} />;
-                            case ColumnType.List:
-                                return <CollectionSchemaListCell {...props} />;
-                            case ColumnType.Date:
-                                return <CollectionSchemaDateCell {...props} />;
-                            default:
-                                return <CollectionSchemaCell {...props} />;
-                        }
-                    },
-                    minWidth: 200,
-                };
-            })
-        );
-        columns.push({
-            Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
-            accessor: (doc: Doc) => 0,
-            id: 'add',
-            Cell: (rowProps: CellInfo) => {
-                const rowIndex = rowProps.index;
-                const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
-                const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
-                return (
-                    <CollectionSchemaButtons
-                        {...{
-                            row: rowProps.index,
-                            col: columnIndex,
-                            rowProps: rowProps,
-                            isFocused: isFocused,
-                            changeFocusedCellByIndex: this.changeFocusedCellByIndex,
-                            CollectionView: this.props.CollectionView,
-                            ContainingCollection: this.props.ContainingCollectionView,
-                            Document: this.props.Document,
-                            fieldKey: this.props.fieldKey,
-                            renderDepth: this.props.renderDepth,
-                            addDocTab: this.props.addDocTab,
-                            pinToPres: this.props.pinToPres,
-                            moveDocument: this.props.moveDocument,
-                            setIsEditing: this.setCellIsEditing,
-                            isEditable: isEditable,
-                            setPreviewDoc: this.props.setPreviewDoc,
-                            setComputed: this.setComputed,
-                            getField: this.getField,
-                            showDoc: this.showDoc,
-                        }}
-                    />
-                );
-            },
-            width: 28,
-            resizable: false,
-        });
-        return columns;
-    }
-
-    constructor(props: SchemaTableProps) {
-        super(props);
-        if (this.props.Document._schemaHeaders === undefined) {
-            this.props.Document._schemaHeaders = new List<SchemaHeaderField>([
-                new SchemaHeaderField('title', '#f1efeb'),
-                new SchemaHeaderField('author', '#f1efeb'),
-                new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date),
-                new SchemaHeaderField('text', '#f1efeb', ColumnType.String),
-                new SchemaHeaderField('type', '#f1efeb'),
-                new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc),
-            ]);
-        }
-    }
-
-    componentDidMount() {
-        document.addEventListener('keydown', this.onKeyDown);
-    }
-
-    componentWillUnmount() {
-        document.removeEventListener('keydown', this.onKeyDown);
-    }
-
-    tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
-        const tableDoc = this.props.Document[DataSym];
-        const effectiveAcl = GetEffectiveAcl(tableDoc);
-
-        if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) {
-            doc.context = this.props.Document;
-            tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
-            return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
-        }
-        return false;
-    };
-
-    private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
-        return !rowInfo
-            ? {}
-            : {
-                  ScreenToLocalTransform: this.props.ScreenToLocalTransform,
-                  addDoc: this.tableAddDoc,
-                  removeDoc: this.props.deleteDocument,
-                  rowInfo,
-                  rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
-                  textWrapRow: this.toggleTextWrapRow,
-                  rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
-                  dropAction: StrCast(this.props.Document.childDropAction),
-                  addDocTab: this.props.addDocTab,
-              };
-    };
-
-    private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
-        if (!rowInfo || column) return {};
-
-        const row = rowInfo.index;
-        //@ts-ignore
-        const col = this.columns.map(c => c.heading).indexOf(column!.id);
-        const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
-        // TODO: editing border doesn't work :(
-        return {
-            style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' },
-        };
-    };
-
-    @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing);
-
-    @action
-    onKeyDown = (e: KeyboardEvent): void => {
-        if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {
-            // && this.props.isSelected(true)) {
-            const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : '';
-            this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
-
-            if (direction) {
-                const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
-                pdoc && this.props.setPreviewDoc(pdoc);
-                e.stopPropagation();
-            }
-        } else if (e.keyCode === 27) {
-            this.props.setPreviewDoc(undefined);
-            e.stopPropagation(); // stopPropagation for left/right arrows
-        }
-    };
-
-    changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
-        switch (direction) {
-            case 'tab':
-                return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
-            case 'right':
-                return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
-            case 'left':
-                return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
-            case 'up':
-                return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
-            case 'down':
-                return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
-        }
-        return this._focusedCell;
-    };
-
-    @action
-    changeFocusedCellByIndex = (row: number, col: number): void => {
-        if (this._focusedCell.row !== row || this._focusedCell.col !== col) {
-            this._focusedCell = { row: row, col: col };
-        }
-        this.props.setFocused(this.props.Document);
-    };
-
-    @undoBatch
-    createRow = action(() => {
-        this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 }));
-        this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
-    });
-
-    @undoBatch
-    @action
-    createColumn = () => {
-        const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`;
-        for (let index = 0; index < 100; index++) {
-            if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) {
-                this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb'));
-                break;
-            }
-        }
-    };
-
-    @action
-    getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => {
-        if (doc && field && column.type === ColumnType.Any) {
-            const val = doc[CollectionSchemaCell.resolvedFieldKey(field, doc)];
-            if (val instanceof ImageField) return ColumnType.Image;
-            if (val instanceof Doc) return ColumnType.Doc;
-            if (val instanceof DateField) return ColumnType.Date;
-            if (val instanceof List) return ColumnType.List;
-        }
-        if (column.type && column.type !== 0) {
-            return column.type;
-        }
-        if (columnTypes.get(column.heading)) {
-            return (column.type = columnTypes.get(column.heading)!);
-        }
-        return (column.type = ColumnType.Any);
-    };
-
-    @undoBatch
-    @action
-    toggleTextwrap = async () => {
-        const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
-        if (textwrappedRows.length) {
-            this.props.Document.textwrappedSchemaRows = new List<string>([]);
-        } else {
-            const docs = DocListCast(this.props.Document[this.props.fieldKey]);
-            const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
-            this.props.Document.textwrappedSchemaRows = new List<string>(allRows);
-        }
-    };
-
-    @action
-    toggleTextWrapRow = (doc: Doc): void => {
-        const textWrapped = this.textWrappedRows;
-        const index = textWrapped.findIndex(id => doc[Id] === id);
-
-        index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
-
-        this.textWrappedRows = textWrapped;
-    };
-
-    @computed
-    get reactTable() {
-        const children = this.childDocs;
-        const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
-        const expanded: { [name: string]: any } = {};
-        Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true));
-        const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
-
-        return (
-            <ReactTable
-                style={{ position: 'relative' }}
-                data={children}
-                page={0}
-                pageSize={children.length}
-                showPagination={false}
-                columns={this.tableColumns}
-                getTrProps={this.getTrProps}
-                getTdProps={this.getTdProps}
-                sortable={false}
-                TrComponent={MovableRow}
-                sorted={this.sorted}
-                expanded={expanded}
-                resized={this.resized}
-                onResizedChange={this.props.onResizedChange}
-                // if it has a child, render another table with the children
-                SubComponent={
-                    !hasCollectionChild
-                        ? undefined
-                        : row =>
-                              row.original.type !== DocumentType.COL ? null : (
-                                  <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub">
-                                      <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} />
-                                  </div>
-                              )
-                }
-            />
-        );
-    }
-
-    onContextMenu = (e: React.MouseEvent): void => {
-        ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' });
-    };
-
-    getField = (row: number, col?: number) => {
-        const docs = this.childDocs;
-
-        row = row % docs.length;
-        while (row < 0) row += docs.length;
-        const columns = this.props.columns;
-        const doc = docs[row];
-        if (col === undefined) {
-            return doc;
-        }
-        if (col >= 0 && col < columns.length) {
-            const column = this.props.columns[col].heading;
-            return doc[column];
-        }
-        return undefined;
-    };
-
-    createTransformer = (row: number, col: number): Transformer => {
-        const self = this;
-        const captures: { [name: string]: Field } = {};
-
-        const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
-            return root => {
-                function visit(node: ts.Node) {
-                    node = ts.visitEachChild(node, visit, context);
-                    if (ts.isIdentifier(node)) {
-                        const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
-                        const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
-                        if (isntPropAccess && isntPropAssign) {
-                            if (node.text === '$r') {
-                                return ts.createNumericLiteral(row.toString());
-                            } else if (node.text === '$c') {
-                                return ts.createNumericLiteral(col.toString());
-                            } else if (node.text === '$') {
-                                if (ts.isCallExpression(node.parent)) {
-                                    // captures.doc = self.props.Document;
-                                    // captures.key = self.props.fieldKey;
-                                }
-                            }
-                        }
-                    }
-
-                    return node;
-                }
-                return ts.visitNode(root, visit);
-            };
-        };
-
-        // const getVars = () => {
-        //     return { capturedVariables: captures };
-        // };
-
-        return { transformer /*getVars*/ };
-    };
-
-    setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
-        script = `const $ = (row:number, col?:number) => {
-                const rval = (doc as any)[key][row + ${row}];
-                return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
-            }
-            return ${script}`;
-        const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
-        if (compiled.compiled) {
-            doc[field] = new ComputedField(compiled);
-            return true;
-        }
-        return false;
-    };
-
-    @action
-    showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => {
-        this._showDoc = doc;
-        if (dataDoc && screenX && screenY) {
-            this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY);
-        }
-    };
-
-    onOpenClick = () => {
-        this._showDoc && this.props.addDocTab(this._showDoc, OpenWhere.addRight);
-    };
-
-    getPreviewTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth);
-    };
-
-    render() {
-        const preview = '';
-        return (
-            <div
-                className="collectionSchemaView-table"
-                onPointerDown={this.props.onPointerDown}
-                onClick={this.props.onClick}
-                onWheel={e => this.props.active(true) && e.stopPropagation()}
-                onDrop={e => this.props.onDrop(e, {})}
-                onContextMenu={this.onContextMenu}>
-                {this.reactTable}
-                {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : (
-                    <div className="collectionSchemaView-addRow" onClick={this.createRow}>
-                        + new
-                    </div>
-                )}
-                {!this._showDoc ? null : (
-                    <div
-                        className="collectionSchemaView-documentPreview"
-                        ref="overlay"
-                        style={{
-                            position: 'absolute',
-                            width: 150,
-                            height: 150,
-                            background: 'dimgray',
-                            display: 'block',
-                            top: 0,
-                            left: 0,
-                            transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`,
-                        }}>
-                        <DocumentView
-                            Document={this._showDoc}
-                            DataDoc={this._showDataDoc}
-                            styleProvider={DefaultStyleProvider}
-                            docViewPath={returnEmptyDoclist}
-                            focus={DocUtils.DefaultFocus}
-                            renderDepth={this.props.renderDepth}
-                            rootSelected={returnFalse}
-                            isContentActive={returnTrue}
-                            isDocumentActive={returnFalse}
-                            PanelWidth={() => 150}
-                            PanelHeight={() => 150}
-                            ScreenToLocalTransform={this.getPreviewTransform}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                            ContainingCollectionView={this.props.CollectionView}
-                            moveDocument={this.props.moveDocument}
-                            whenChildContentsActiveChanged={emptyFunction}
-                            addDocTab={this.props.addDocTab}
-                            pinToPres={this.props.pinToPres}
-                            bringToFront={returnFalse}></DocumentView>
-                    </div>
-                )}
-            </div>
-        );
-    }
-}
+// import { IconProp } from '@fortawesome/fontawesome-svg-core';
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { action, computed, observable } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table';
+// import { DateField } from '../../../../fields/DateField';
+// import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { List } from '../../../../fields/List';
+// import { listSpec } from '../../../../fields/Schema';
+// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+// import { ComputedField } from '../../../../fields/ScriptField';
+// import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+// import { ImageField } from '../../../../fields/URLField';
+// import { GetEffectiveAcl } from '../../../../fields/util';
+// import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils';
+// import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+// import { DocumentType } from '../../../documents/DocumentTypes';
+// import { CompileScript, Transformer, ts } from '../../../util/Scripting';
+// import { Transform } from '../../../util/Transform';
+// import { undoBatch } from '../../../util/UndoManager';
+// import '../../../views/DocumentDecorations.scss';
+// import { ContextMenu } from '../../ContextMenu';
+// import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
+// import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
+// import { PinProps } from '../../nodes/trails';
+// import { DefaultStyleProvider } from '../../StyleProvider';
+// import { CollectionView } from '../CollectionView';
+// import {
+//     CellProps,
+//     CollectionSchemaButtons,
+//     CollectionSchemaCell,
+//     CollectionSchemaCheckboxCell,
+//     CollectionSchemaDateCell,
+//     CollectionSchemaDocCell,
+//     CollectionSchemaImageCell,
+//     CollectionSchemaListCell,
+//     CollectionSchemaNumberCell,
+//     CollectionSchemaStringCell,
+// } from './CollectionSchemaCells';
+// import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders';
+// import { MovableColumn } from './OldCollectionSchemaMovableColumn';
+// import { MovableRow } from './CollectionSchemaMovableRow';
+// import './CollectionSchemaView.scss';
+
+// enum ColumnType {
+//     Any,
+//     Number,
+//     String,
+//     Boolean,
+//     Doc,
+//     Image,
+//     List,
+//     Date,
+// }
+
+// // this map should be used for keys that should have a const type of value
+// const columnTypes: Map<string, ColumnType> = new Map([
+//     ['title', ColumnType.String],
+//     ['x', ColumnType.Number],
+//     ['y', ColumnType.Number],
+//     ['_width', ColumnType.Number],
+//     ['_height', ColumnType.Number],
+//     ['_nativeWidth', ColumnType.Number],
+//     ['_nativeHeight', ColumnType.Number],
+//     ['isPrototype', ColumnType.Boolean],
+//     ['_curPage', ColumnType.Number],
+//     ['_currentTimecode', ColumnType.Number],
+//     ['zIndex', ColumnType.Number],
+// ]);
+
+// export interface SchemaTableProps {
+//     Document: Doc; // child doc
+//     dataDoc?: Doc;
+//     PanelHeight: () => number;
+//     PanelWidth: () => number;
+//     childDocs?: Doc[];
+//     CollectionView: Opt<CollectionView>;
+//     ContainingCollectionView: Opt<CollectionView>;
+//     ContainingCollectionDoc: Opt<Doc>;
+//     fieldKey: string;
+//     renderDepth: number;
+//     deleteDocument?: (document: Doc | Doc[]) => boolean;
+//     addDocument?: (document: Doc | Doc[]) => boolean;
+//     moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+//     ScreenToLocalTransform: () => Transform;
+//     active: (outsideReaction: boolean | undefined) => boolean | undefined;
+//     onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
+//     addDocTab: (document: Doc, where: OpenWhere) => boolean;
+//     pinToPres: (document: Doc, pinProps: PinProps) => void;
+//     isSelected: (outsideReaction?: boolean) => boolean;
+//     isFocused: (document: Doc, outsideReaction: boolean) => boolean;
+//     setFocused: (document: Doc) => void;
+//     setPreviewDoc: (document: Opt<Doc>) => void;
+//     columns: SchemaHeaderField[];
+//     documentKeys: any[];
+//     headerIsEditing: boolean;
+//     openHeader: (column: any, screenx: number, screeny: number) => void;
+//     onClick: (e: React.MouseEvent) => void;
+//     onPointerDown: (e: React.PointerEvent) => void;
+//     onResizedChange: (newResized: Resize[], event: any) => void;
+//     setColumns: (columns: SchemaHeaderField[]) => void;
+//     reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => void;
+//     changeColumns: (oldKey: string, newKey: string, addNew: boolean) => void;
+//     setHeaderIsEditing: (isEditing: boolean) => void;
+//     changeColumnSort: (columnField: SchemaHeaderField, descending: boolean | undefined) => void;
+// }
+
+// @observer
+// export class SchemaTable extends React.Component<SchemaTableProps> {
+//     @observable _cellIsEditing: boolean = false;
+//     @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 };
+//     @observable _openCollections: Set<number> = new Set();
+
+//     @observable _showDoc: Doc | undefined;
+//     @observable _showDataDoc: any = '';
+//     @observable _showDocPos: number[] = [];
+
+//     @observable _showTitleDropdown: boolean = false;
+
+//     @computed get previewWidth() {
+//         return () => NumCast(this.props.Document.schemaPreviewWidth);
+//     }
+//     @computed get previewHeight() {
+//         return () => this.props.PanelHeight() - 2 * this.borderWidth;
+//     }
+//     @computed get tableWidth() {
+//         return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
+//     }
+
+//     @computed get childDocs() {
+//         if (this.props.childDocs) return this.props.childDocs;
+
+//         const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+//         return DocListCast(doc[this.props.fieldKey]);
+//     }
+//     set childDocs(docs: Doc[]) {
+//         const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+//         doc[this.props.fieldKey] = new List<Doc>(docs);
+//     }
+
+//     @computed get textWrappedRows() {
+//         return Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
+//     }
+//     set textWrappedRows(textWrappedRows: string[]) {
+//         this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows);
+//     }
+
+//     @computed get resized(): { id: string; value: number }[] {
+//         return this.props.columns.reduce((resized, shf) => {
+//             shf.width > -1 && resized.push({ id: shf.heading, value: shf.width });
+//             return resized;
+//         }, [] as { id: string; value: number }[]);
+//     }
+//     @computed get sorted(): SortingRule[] {
+//         return this.props.columns.reduce((sorted, shf) => {
+//             shf.desc !== undefined && sorted.push({ id: shf.heading, desc: shf.desc });
+//             return sorted;
+//         }, [] as SortingRule[]);
+//     }
+
+//     @action
+//     changeSorting = (col: any) => {
+//         this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
+//     };
+
+//     @action
+//     changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown);
+
+//     @computed get borderWidth() {
+//         return Number(COLLECTION_BORDER_WIDTH);
+//     }
+//     @computed get tableColumns(): Column<Doc>[] {
+//         const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
+//         const columns: Column<Doc>[] = [];
+//         const tableIsFocused = this.props.isFocused(this.props.Document, false);
+//         const focusedRow = this._focusedCell.row;
+//         const focusedCol = this._focusedCell.col;
+//         const isEditable = !this.props.headerIsEditing;
+
+//         columns.push({
+//             expander: true,
+//             Header: '',
+//             width: 58,
+//             Expander: rowInfo => {
+//                 return rowInfo.original.type !== DocumentType.COL ? null : (
+//                     <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}>
+//                         <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" />
+//                     </div>
+//                 );
+//             },
+//         });
+//         columns.push(
+//             ...this.props.columns.map(col => {
+//                 const icon: IconProp =
+//                     this.getColumnType(col) === ColumnType.Number
+//                         ? 'hashtag'
+//                         : this.getColumnType(col) === ColumnType.String
+//                         ? 'font'
+//                         : this.getColumnType(col) === ColumnType.Boolean
+//                         ? 'check-square'
+//                         : this.getColumnType(col) === ColumnType.Doc
+//                         ? 'file'
+//                         : this.getColumnType(col) === ColumnType.Image
+//                         ? 'image'
+//                         : this.getColumnType(col) === ColumnType.List
+//                         ? 'list-ul'
+//                         : this.getColumnType(col) === ColumnType.Date
+//                         ? 'calendar'
+//                         : 'align-justify';
+
+//                 const keysDropdown = (
+//                     <KeysDropdown
+//                         keyValue={col.heading}
+//                         possibleKeys={possibleKeys}
+//                         existingKeys={this.props.columns.map(c => c.heading)}
+//                         canAddNew={true}
+//                         addNew={false}
+//                         onSelect={this.props.changeColumns}
+//                         setIsEditing={this.props.setHeaderIsEditing}
+//                         docs={this.props.childDocs}
+//                         Document={this.props.Document}
+//                         dataDoc={this.props.dataDoc}
+//                         fieldKey={this.props.fieldKey}
+//                         ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+//                         ContainingCollectionView={this.props.ContainingCollectionView}
+//                         active={this.props.active}
+//                         openHeader={this.props.openHeader}
+//                         icon={icon}
+//                         col={col}
+//                         // try commenting this out
+//                         width={'100%'}
+//                     />
+//                 );
+
+//                 const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up';
+//                 const header = (
+//                     <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}>
+//                         {keysDropdown}
+//                         <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}>
+//                             <FontAwesomeIcon icon={sortIcon} size="lg" />
+//                         </div>
+//                         {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
+//                     </div>
+//                 );
+
+//                 return {
+//                     Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
+//                     accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0),
+//                     id: col.heading,
+//                     Cell: (rowProps: CellInfo) => {
+//                         const rowIndex = rowProps.index;
+//                         const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+//                         const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+
+//                         const props: CellProps = {
+//                             row: rowIndex,
+//                             col: columnIndex,
+//                             rowProps: rowProps,
+//                             isFocused: isFocused,
+//                             changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+//                             CollectionView: this.props.CollectionView,
+//                             ContainingCollection: this.props.ContainingCollectionView,
+//                             Document: this.props.Document,
+//                             fieldKey: this.props.fieldKey,
+//                             renderDepth: this.props.renderDepth,
+//                             addDocTab: this.props.addDocTab,
+//                             pinToPres: this.props.pinToPres,
+//                             moveDocument: this.props.moveDocument,
+//                             setIsEditing: this.setCellIsEditing,
+//                             isEditable: isEditable,
+//                             setPreviewDoc: this.props.setPreviewDoc,
+//                             setComputed: this.setComputed,
+//                             getField: this.getField,
+//                             showDoc: this.showDoc,
+//                         };
+
+//                         switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
+//                             case ColumnType.Number:
+//                                 return <CollectionSchemaNumberCell {...props} />;
+//                             case ColumnType.String:
+//                                 return <CollectionSchemaStringCell {...props} />;
+//                             case ColumnType.Boolean:
+//                                 return <CollectionSchemaCheckboxCell {...props} />;
+//                             case ColumnType.Doc:
+//                                 return <CollectionSchemaDocCell {...props} />;
+//                             case ColumnType.Image:
+//                                 return <CollectionSchemaImageCell {...props} />;
+//                             case ColumnType.List:
+//                                 return <CollectionSchemaListCell {...props} />;
+//                             case ColumnType.Date:
+//                                 return <CollectionSchemaDateCell {...props} />;
+//                             default:
+//                                 return <CollectionSchemaCell {...props} />;
+//                         }
+//                     },
+//                     minWidth: 200,
+//                 };
+//             })
+//         );
+//         columns.push({
+//             Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
+//             accessor: (doc: Doc) => 0,
+//             id: 'add',
+//             Cell: (rowProps: CellInfo) => {
+//                 const rowIndex = rowProps.index;
+//                 const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+//                 const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+//                 return (
+//                     <CollectionSchemaButtons
+//                         {...{
+//                             row: rowProps.index,
+//                             col: columnIndex,
+//                             rowProps: rowProps,
+//                             isFocused: isFocused,
+//                             changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+//                             CollectionView: this.props.CollectionView,
+//                             ContainingCollection: this.props.ContainingCollectionView,
+//                             Document: this.props.Document,
+//                             fieldKey: this.props.fieldKey,
+//                             renderDepth: this.props.renderDepth,
+//                             addDocTab: this.props.addDocTab,
+//                             pinToPres: this.props.pinToPres,
+//                             moveDocument: this.props.moveDocument,
+//                             setIsEditing: this.setCellIsEditing,
+//                             isEditable: isEditable,
+//                             setPreviewDoc: this.props.setPreviewDoc,
+//                             setComputed: this.setComputed,
+//                             getField: this.getField,
+//                             showDoc: this.showDoc,
+//                         }}
+//                     />
+//                 );
+//             },
+//             width: 28,
+//             resizable: false,
+//         });
+//         return columns;
+//     }
+
+//     constructor(props: SchemaTableProps) {
+//         super(props);
+//         if (this.props.Document._schemaHeaders === undefined) {
+//             this.props.Document._schemaHeaders = new List<SchemaHeaderField>([
+//                 new SchemaHeaderField('title', '#f1efeb'),
+//                 new SchemaHeaderField('author', '#f1efeb'),
+//                 new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date),
+//                 new SchemaHeaderField('text', '#f1efeb', ColumnType.String),
+//                 new SchemaHeaderField('type', '#f1efeb'),
+//                 new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc),
+//             ]);
+//         }
+//     }
+
+//     componentDidMount() {
+//         document.addEventListener('keydown', this.onKeyDown);
+//     }
+
+//     componentWillUnmount() {
+//         document.removeEventListener('keydown', this.onKeyDown);
+//     }
+
+//     tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
+//         const tableDoc = this.props.Document[DataSym];
+//         const effectiveAcl = GetEffectiveAcl(tableDoc);
+
+//         if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) {
+//             doc.context = this.props.Document;
+//             tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
+//             return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
+//         }
+//         return false;
+//     };
+
+//     private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
+//         return !rowInfo
+//             ? {}
+//             : {
+//                   ScreenToLocalTransform: this.props.ScreenToLocalTransform,
+//                   addDoc: this.tableAddDoc,
+//                   removeDoc: this.props.deleteDocument,
+//                   rowInfo,
+//                   rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
+//                   textWrapRow: this.toggleTextWrapRow,
+//                   rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
+//                   dropAction: StrCast(this.props.Document.childDropAction),
+//                   addDocTab: this.props.addDocTab,
+//               };
+//     };
+
+//     private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
+//         if (!rowInfo || column) return {};
+
+//         const row = rowInfo.index;
+//         //@ts-ignore
+//         const col = this.columns.map(c => c.heading).indexOf(column!.id);
+//         const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
+//         // TODO: editing border doesn't work :(
+//         return {
+//             style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' },
+//         };
+//     };
+
+//     @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing);
+
+//     @action
+//     onKeyDown = (e: KeyboardEvent): void => {
+//         if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {
+//             // && this.props.isSelected(true)) {
+//             const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : '';
+//             this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
+
+//             if (direction) {
+//                 const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
+//                 pdoc && this.props.setPreviewDoc(pdoc);
+//                 e.stopPropagation();
+//             }
+//         } else if (e.keyCode === 27) {
+//             this.props.setPreviewDoc(undefined);
+//             e.stopPropagation(); // stopPropagation for left/right arrows
+//         }
+//     };
+
+//     changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
+//         switch (direction) {
+//             case 'tab':
+//                 return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
+//             case 'right':
+//                 return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
+//             case 'left':
+//                 return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
+//             case 'up':
+//                 return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
+//             case 'down':
+//                 return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
+//         }
+//         return this._focusedCell;
+//     };
+
+//     @action
+//     changeFocusedCellByIndex = (row: number, col: number): void => {
+//         if (this._focusedCell.row !== row || this._focusedCell.col !== col) {
+//             this._focusedCell = { row: row, col: col };
+//         }
+//         this.props.setFocused(this.props.Document);
+//     };
+
+//     @undoBatch
+//     createRow = action(() => {
+//         this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 }));
+//         this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
+//     });
+
+//     @undoBatch
+//     @action
+//     createColumn = () => {
+//         const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`;
+//         for (let index = 0; index < 100; index++) {
+//             if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) {
+//                 this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb'));
+//                 break;
+//             }
+//         }
+//     };
+
+//     @action
+//     getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => {
+//         if (doc && field && column.type === ColumnType.Any) {
+//             const val = doc[CollectionSchemaCell.resolvedFieldKey(field, doc)];
+//             if (val instanceof ImageField) return ColumnType.Image;
+//             if (val instanceof Doc) return ColumnType.Doc;
+//             if (val instanceof DateField) return ColumnType.Date;
+//             if (val instanceof List) return ColumnType.List;
+//         }
+//         if (column.type && column.type !== 0) {
+//             return column.type;
+//         }
+//         if (columnTypes.get(column.heading)) {
+//             return (column.type = columnTypes.get(column.heading)!);
+//         }
+//         return (column.type = ColumnType.Any);
+//     };
+
+//     @undoBatch
+//     @action
+//     toggleTextwrap = async () => {
+//         const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
+//         if (textwrappedRows.length) {
+//             this.props.Document.textwrappedSchemaRows = new List<string>([]);
+//         } else {
+//             const docs = DocListCast(this.props.Document[this.props.fieldKey]);
+//             const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
+//             this.props.Document.textwrappedSchemaRows = new List<string>(allRows);
+//         }
+//     };
+
+//     @action
+//     toggleTextWrapRow = (doc: Doc): void => {
+//         const textWrapped = this.textWrappedRows;
+//         const index = textWrapped.findIndex(id => doc[Id] === id);
+
+//         index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
+
+//         this.textWrappedRows = textWrapped;
+//     };
+
+//     @computed
+//     get reactTable() {
+//         const children = this.childDocs;
+//         const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
+//         const expanded: { [name: string]: any } = {};
+//         Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true));
+//         const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
+
+//         return (
+//             <ReactTable
+//                 style={{ position: 'relative' }}
+//                 data={children}
+//                 page={0}
+//                 pageSize={children.length}
+//                 showPagination={false}
+//                 columns={this.tableColumns}
+//                 getTrProps={this.getTrProps}
+//                 getTdProps={this.getTdProps}
+//                 sortable={false}
+//                 TrComponent={MovableRow}
+//                 sorted={this.sorted}
+//                 expanded={expanded}
+//                 resized={this.resized}
+//                 onResizedChange={this.props.onResizedChange}
+//                 // if it has a child, render another table with the children
+//                 SubComponent={
+//                     !hasCollectionChild
+//                         ? undefined
+//                         : row =>
+//                               row.original.type !== DocumentType.COL ? null : (
+//                                   <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub">
+//                                       <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} />
+//                                   </div>
+//                               )
+//                 }
+//             />
+//         );
+//     }
+
+//     onContextMenu = (e: React.MouseEvent): void => {
+//         ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' });
+//     };
+
+//     getField = (row: number, col?: number) => {
+//         const docs = this.childDocs;
+
+//         row = row % docs.length;
+//         while (row < 0) row += docs.length;
+//         const columns = this.props.columns;
+//         const doc = docs[row];
+//         if (col === undefined) {
+//             return doc;
+//         }
+//         if (col >= 0 && col < columns.length) {
+//             const column = this.props.columns[col].heading;
+//             return doc[column];
+//         }
+//         return undefined;
+//     };
+
+//     createTransformer = (row: number, col: number): Transformer => {
+//         const self = this;
+//         const captures: { [name: string]: Field } = {};
+
+//         const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
+//             return root => {
+//                 function visit(node: ts.Node) {
+//                     node = ts.visitEachChild(node, visit, context);
+//                     if (ts.isIdentifier(node)) {
+//                         const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
+//                         const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
+//                         if (isntPropAccess && isntPropAssign) {
+//                             if (node.text === '$r') {
+//                                 return ts.createNumericLiteral(row.toString());
+//                             } else if (node.text === '$c') {
+//                                 return ts.createNumericLiteral(col.toString());
+//                             } else if (node.text === '$') {
+//                                 if (ts.isCallExpression(node.parent)) {
+//                                     // captures.doc = self.props.Document;
+//                                     // captures.key = self.props.fieldKey;
+//                                 }
+//                             }
+//                         }
+//                     }
+
+//                     return node;
+//                 }
+//                 return ts.visitNode(root, visit);
+//             };
+//         };
+
+//         // const getVars = () => {
+//         //     return { capturedVariables: captures };
+//         // };
+
+//         return { transformer /*getVars*/ };
+//     };
+
+//     setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
+//         script = `const $ = (row:number, col?:number) => {
+//                 const rval = (doc as any)[key][row + ${row}];
+//                 return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
+//             }
+//             return ${script}`;
+//         const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
+//         if (compiled.compiled) {
+//             doc[field] = new ComputedField(compiled);
+//             return true;
+//         }
+//         return false;
+//     };
+
+//     @action
+//     showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => {
+//         this._showDoc = doc;
+//         if (dataDoc && screenX && screenY) {
+//             this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY);
+//         }
+//     };
+
+//     onOpenClick = () => {
+//         this._showDoc && this.props.addDocTab(this._showDoc, OpenWhere.addRight);
+//     };
+
+//     getPreviewTransform = (): Transform => {
+//         return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth);
+//     };
+
+//     render() {
+//         const preview = '';
+//         return (
+//             <div
+//                 className="collectionSchemaView-table"
+//                 onPointerDown={this.props.onPointerDown}
+//                 onClick={this.props.onClick}
+//                 onWheel={e => this.props.active(true) && e.stopPropagation()}
+//                 onDrop={e => this.props.onDrop(e, {})}
+//                 onContextMenu={this.onContextMenu}>
+//                 {this.reactTable}
+//                 {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : (
+//                     <div className="collectionSchemaView-addRow" onClick={this.createRow}>
+//                         + new
+//                     </div>
+//                 )}
+//                 {!this._showDoc ? null : (
+//                     <div
+//                         className="collectionSchemaView-documentPreview"
+//                         ref="overlay"
+//                         style={{
+//                             position: 'absolute',
+//                             width: 150,
+//                             height: 150,
+//                             background: 'dimgray',
+//                             display: 'block',
+//                             top: 0,
+//                             left: 0,
+//                             transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`,
+//                         }}>
+//                         <DocumentView
+//                             Document={this._showDoc}
+//                             DataDoc={this._showDataDoc}
+//                             styleProvider={DefaultStyleProvider}
+//                             docViewPath={returnEmptyDoclist}
+//                             focus={DocUtils.DefaultFocus}
+//                             renderDepth={this.props.renderDepth}
+//                             rootSelected={returnFalse}
+//                             isContentActive={returnTrue}
+//                             isDocumentActive={returnFalse}
+//                             PanelWidth={() => 150}
+//                             PanelHeight={() => 150}
+//                             ScreenToLocalTransform={this.getPreviewTransform}
+//                             docFilters={returnEmptyFilter}
+//                             docRangeFilters={returnEmptyFilter}
+//                             searchFilterDocs={returnEmptyDoclist}
+//                             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+//                             ContainingCollectionView={this.props.CollectionView}
+//                             moveDocument={this.props.moveDocument}
+//                             whenChildContentsActiveChanged={emptyFunction}
+//                             addDocTab={this.props.addDocTab}
+//                             pinToPres={this.props.pinToPres}
+//                             bringToFront={returnFalse}></DocumentView>
+//                     </div>
+//                 )}
+//             </div>
+//         );
+//     }
+// }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 569579996..459554ebe 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,6 +44,7 @@ import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
 import { LoadingBox } from './LoadingBox';
+import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -268,6 +269,7 @@ export class DocumentContentsView extends React.Component<
                     HTMLtag,
                     ComparisonBox,
                     LoadingBox,
+                    SchemaRowBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From 6c58ca9d473103624be82c6f2da90f22bafd7b98 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Sat, 28 Jan 2023 23:27:17 -0500
Subject: version without schemarow as documentview

---
 src/client/documents/DocumentTypes.ts              |  1 -
 src/client/documents/Documents.ts                  | 11 ---
 .../collectionSchema/CollectionSchemaView.scss     |  6 --
 .../collectionSchema/CollectionSchemaView.tsx      | 94 ++++++----------------
 .../collectionSchema/SchemaColumnHeader.tsx        |  2 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  | 88 +++++++-------------
 src/client/views/nodes/DocumentContentsView.tsx    |  2 -
 7 files changed, 53 insertions(+), 151 deletions(-)

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

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index b910b26b0..d99cd2dac 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -40,7 +40,6 @@ export enum DocumentType {
     SEARCHITEM = 'searchitem',
     COMPARISON = 'comparison',
     GROUP = 'group',
-    SCHEMAROW = 'schemarow',
 
     LINKDB = 'linkdb', // database of links  ??? why do we have this
     SCRIPTDB = 'scriptdb', // database of scripts
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f6104d655..7de1221cc 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -26,7 +26,6 @@ import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
-import { SchemaRowBox } from '../views/collections/collectionSchema/SchemaRowBox';
 import { CollectionView } from '../views/collections/CollectionView';
 import { ContextMenu } from '../views/ContextMenu';
 import { ContextMenuProps } from '../views/ContextMenuItem';
@@ -650,12 +649,6 @@ export namespace Docs {
                     options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' },
                 },
             ],
-            [
-                DocumentType.SCHEMAROW,
-                {
-                    layout: { view: SchemaRowBox, dataField: defaultDataKey },
-                },
-            ],
             [
                 DocumentType.LOADING,
                 {
@@ -1148,10 +1141,6 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options });
         }
 
-        export function SchemaRowDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCHEMAROW), undefined, { ...(options || {}) });
-        }
-
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, viewType: CollectionViewType.Docking, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index e0d0101a2..4fa5d80e2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -126,16 +126,10 @@
     }
 }
 
-.schema-row-wrapper {
-    max-height: 70px;
-    overflow: hidden;
-}
-
 .schema-header-row,
 .schema-row {
     display: flex;
     flex-direction: row;
-    height: 100%;
     max-height: 70px;
     overflow: auto;
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index c9f934aec..29b22c0d5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,29 +1,28 @@
 import React = require('react');
-import { action, computed, observable, ObservableMap, ObservableSet, trace, untracked } from 'mobx';
+import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, DocListCast, Opt, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyString, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
+import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { EditableView } from '../../EditableView';
+import { DocumentView } from '../../nodes/DocumentView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
-import { DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { Transform } from '../../../util/Transform';
 
 export enum ColumnType {
     Number,
@@ -33,7 +32,7 @@ export enum ColumnType {
     Image,
 }
 
-const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data', 'tags'];
+const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data', 'creationDate'];
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
@@ -399,25 +398,6 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
-    focusDocument = (doc: Doc, options: DocFocusOptions) => {
-        Doc.BrushDoc(doc);
-
-        let focusSpeed = 0;
-        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
-        if (found) {
-            const top = found.getBoundingClientRect().top;
-            const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
-            if (Math.floor(localTop[1]) !== 0) {
-                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
-            }
-        }
-        const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
-        this.props.focus(this.rootDoc, {
-            ...options,
-            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
-        });
-    };
-
     isChildContentActive = () =>
         this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
 
@@ -465,52 +445,24 @@ export class CollectionSchemaView extends CollectionSubView() {
                             let dref: Opt<DocumentView>;
 
                             return (
-                                <div className="schema-row-wrapper">
-                                    <DocumentView
-                                        {...this.props}
-                                        ref={r => (dref = r || undefined)}
-                                        LayoutTemplate={this.props.childLayoutTemplate}
-                                        LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
-                                        renderDepth={this.props.renderDepth + 1}
-                                        Document={doc}
-                                        DataDoc={dataDoc}
-                                        ContainingCollectionView={this.props.CollectionView}
-                                        ContainingCollectionDoc={this.Document}
-                                        PanelWidth={this.props.PanelWidth}
-                                        PanelHeight={() => 70}
-                                        styleProvider={DefaultStyleProvider}
-                                        focus={this.focusDocument}
-                                        docFilters={this.childDocFilters}
-                                        docRangeFilters={this.childDocRangeFilters}
-                                        searchFilterDocs={this.searchFilterDocs}
-                                        rootSelected={this.rootSelected}
-                                        ScreenToLocalTransform={() => this.getDocTransform(doc, dref)}
-                                        bringToFront={emptyFunction}
-                                        isContentActive={this.isChildContentActive}
-                                        hideDecorations={true}
-                                        hideTitle={true}
-                                        hideDocumentButtonBar={true}
-                                    />
-                                </div>
+                                <SchemaRowBox
+                                    {...this.props}
+                                    key={index}
+                                    Document={doc}
+                                    ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                                    ContainingCollectionView={this.props.CollectionView}
+                                    rowIndex={index}
+                                    columnKeys={this.columnKeys}
+                                    columnWidths={this.displayColumnWidths}
+                                    rowMenuWidth={CollectionSchemaView._rowMenuWidth}
+                                    selectedDocs={this._selectedDocs}
+                                    selectRow={this.selectRow}
+                                    startDrag={this.startDrag}
+                                    dragging={this._isDragging}
+                                    dropIndex={this.setDropIndex}
+                                    addRowRef={this.addRowRef}
+                                />
                             );
-
-                            // <SchemaRowBox
-                            //     {...this.props}
-                            //     key={index}
-                            //     Document={doc}
-                            //     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                            //     ContainingCollectionView={this.props.CollectionView}
-                            //     rowIndex={index}
-                            //     columnKeys={this.columnKeys}
-                            //     columnWidths={this.displayColumnWidths}
-                            //     rowMenuWidth={CollectionSchemaView._rowMenuWidth}
-                            //     selectedDocs={this._selectedDocs}
-                            //     selectRow={this.selectRow}
-                            //     startDrag={this.startDrag}
-                            //     dragging={this._isDragging}
-                            //     dropIndex={this.setDropIndex}
-                            //     addRowRef={this.addRowRef}
-                            // />
                         })}
                     </div>
                 </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index a6a5f66ab..8e6d3d78a 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -40,7 +40,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
             case ColumnType.Boolean:
                 return (
                     <>
-                        <input type="checkbox" name="" id="" value={this._newFieldDefault ?? false} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />
+                        <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} />
                         {this._newFieldDefault ? 'true' : 'false'}
                     </>
                 );
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index f790e9dbf..2cf0a1b79 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,20 +1,17 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, ObservableMap, ObservableSet } from 'mobx';
+import { action, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, StrListCast } from '../../../../fields/Doc';
+import { Doc } from '../../../../fields/Doc';
+import { DragManager } from '../../../util/DragManager';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
-import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { OpenWhere } from '../../nodes/DocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
-import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
-import { DragManager } from '../../../util/DragManager';
-import { OpenWhere } from '../../nodes/DocumentView';
-import { Cast } from '../../../../fields/Types';
-import { listSpec } from '../../../../fields/Schema';
-import { CollectionSchemaView } from './CollectionSchemaView';
+import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     rowIndex: number;
@@ -30,61 +27,37 @@ export interface SchemaRowBoxProps extends FieldViewProps {
 }
 
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(SchemaRowBox, fieldKey);
-    }
-
+export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
     private _ref: HTMLDivElement | null = null;
 
     bounds = () => this._ref?.getBoundingClientRect();
 
-    @computed get columnKeys() {
-        return StrListCast(this.props.ContainingCollectionDoc?.columnKeys);
-    }
-
-    @computed get storedColumnWidths() {
-        let widths = Cast(
-            this.props.ContainingCollectionDoc?.columnWidths,
-            listSpec('number'),
-            this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
-        );
-
-        const totalWidth = widths.reduce((sum, width) => sum + width, 0);
-        if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) {
-            widths = widths.map(w => {
-                const proportion = w / totalWidth;
-                return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth);
-            });
-        }
-
-        return widths;
-    }
+    isSelected = () => this.props.selectedDocs.has(this.props.Document);
 
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
 
-        // setupMoveUpEvents(
-        //     this,
-        //     e,
-        //     e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
-        //     emptyFunction,
-        //     e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
-        // );
+        setupMoveUpEvents(
+            this,
+            e,
+            e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
+            emptyFunction,
+            e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
+        );
     };
 
     onPointerEnter = (e: any) => {
-        // if (!this.props.dragging) return;
+        if (!this.props.dragging) return;
         document.removeEventListener('pointermove', this.onPointerMove);
         document.addEventListener('pointermove', this.onPointerMove);
     };
 
     onPointerMove = (e: any) => {
-        // if (!this.props.dragging) return;
+        if (!this.props.dragging) return;
         let dragIsRow: boolean = true;
         DragManager.docsBeingDragged.forEach(doc => {
-            // dragIsRow = this.props.selectedDocs.has(doc);
+            dragIsRow = this.props.selectedDocs.has(doc);
         });
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
@@ -94,11 +67,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (y <= halfLine) {
                 this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
                 this._ref.style.borderBottom = '0px';
-                // this.props.dropIndex(this.props.rowIndex);
+                this.props.dropIndex(this.props.rowIndex);
             } else if (y > halfLine) {
                 this._ref.style.borderTop = '0px';
                 this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                // this.props.dropIndex(this.props.rowIndex + 1);
+                this.props.dropIndex(this.props.rowIndex + 1);
             }
         }
     };
@@ -115,22 +88,19 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="schema-row"
-                style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE } : {}}
-                // style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
-                // onPointerDown={this.onRowPointerDown}
-                // onPointerEnter={this.onPointerEnter}
+                style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
+                onPointerDown={this.onRowPointerDown}
+                onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    // row && this.props.addRowRef(this.props.Document, row);
+                    row && this.props.addRowRef(this.props.Document, row);
                     this._ref = row;
                 }}>
                 <div
                     className="row-menu"
-                    style={
-                        {
-                            /* width: this.props.rowMenuWidth */
-                        }
-                    }>
+                    style={{
+                        width: this.props.rowMenuWidth,
+                    }}>
                     <div
                         className="schema-row-button"
                         onPointerDown={undoBatch(e => {
@@ -149,8 +119,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </div>
                 </div>
                 <div className="row-cells">
-                    {this.columnKeys.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.storedColumnWidths[index]} />
+                    {this.props.columnKeys.map((key, index) => (
+                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 459554ebe..569579996 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,7 +44,6 @@ import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
 import { LoadingBox } from './LoadingBox';
-import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -269,7 +268,6 @@ export class DocumentContentsView extends React.Component<
                     HTMLtag,
                     ComparisonBox,
                     LoadingBox,
-                    SchemaRowBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From 222f659b8c291fafce2648e367392dd9f467cb25 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 27 Feb 2023 17:00:49 -0500
Subject: rows are documentviews but clipped

---
 package-lock.json                                  |  39 +++++++
 .../collectionSchema/CollectionSchemaView.scss     |   6 ++
 .../collectionSchema/CollectionSchemaView.tsx      |  96 ++++++++++++-----
 .../collections/collectionSchema/SchemaRowBox.tsx  | 120 ++++++++++++---------
 .../collectionSchema/SchemaTableCell.tsx           |  12 ++-
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +
 6 files changed, 195 insertions(+), 80 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 3c2dccbf8..8806887df 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5308,6 +5308,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6475,6 +6485,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6486,6 +6518,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -21371,6 +21404,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index bd0fc11b3..46c2e2d1a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -134,10 +134,16 @@
     }
 }
 
+.schema-row-wrapper {
+    max-height: 70px;
+    overflow: hidden;
+}
+
 .schema-header-row,
 .schema-row {
     display: flex;
     flex-direction: row;
+    height: 100%;
     max-height: 70px;
     overflow: auto;
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 64c39cf1a..46510b6fe 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -8,7 +8,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -17,7 +17,7 @@ import { undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { EditableView } from '../../EditableView';
-import { DocumentView } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
@@ -41,9 +41,9 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _lastSelectedRow: number | undefined;
     private _selectedDocSortedArray: Doc[] = [];
     private _closestDropIndex: number = 0;
-    private _minColWidth: number = 150;
     private _previewRef: HTMLDivElement | null = null;
 
+    public static _minColWidth: number = 150;
     public static _rowMenuWidth: number = 100;
     public static _previewDividerWidth: number = 4;
     @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
@@ -110,6 +110,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     componentDidMount() {
+        this.props.setContentView?.(this);
         document.addEventListener('keydown', this.onKeyDown);
     }
 
@@ -243,8 +244,8 @@ export class CollectionSchemaView extends CollectionSubView() {
             if (shrinking === undefined || growing === undefined) return true;
 
             change = Math.abs(change);
-            if (this._displayColumnWidths[shrinking] - change < this._minColWidth) {
-                change = this._displayColumnWidths[shrinking] - this._minColWidth;
+            if (this._displayColumnWidths[shrinking] - change < CollectionSchemaView._minColWidth) {
+                change = this._displayColumnWidths[shrinking] - CollectionSchemaView._minColWidth;
             }
 
             this._displayColumnWidths[shrinking] -= change;
@@ -481,7 +482,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true });
+            const created = Docs.Create.TextDocument('', { title: name, _autoHeight: true });
             if (created) {
                 if (this.props.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.Document);
@@ -492,9 +493,25 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
+    focusDocument = (doc: Doc, options: DocFocusOptions) => {
+        Doc.BrushDoc(doc);
+        let focusSpeed = 0;
+        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
+        if (found) {
+            const top = found.getBoundingClientRect().top;
+            const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
+            if (Math.floor(localTop[1]) !== 0) {
+                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
+            }
+        }
+        const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
+        this.props.focus(this.rootDoc, {
+            ...options,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
+        });
+    };
     isChildContentActive = () =>
         this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
-
     getDocTransform(doc: Doc, dref?: DocumentView) {
         const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
         // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
@@ -543,26 +560,55 @@ export class CollectionSchemaView extends CollectionSubView() {
                         {this.childDocs.map((doc: Doc, index: number) => {
                             const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
                             let dref: Opt<DocumentView>;
-
                             return (
-                                <SchemaRowBox
-                                    {...this.props}
-                                    key={index}
-                                    Document={doc}
-                                    ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                                    ContainingCollectionView={this.props.CollectionView}
-                                    rowIndex={index}
-                                    columnKeys={this.columnKeys}
-                                    columnWidths={this.displayColumnWidths}
-                                    rowMenuWidth={CollectionSchemaView._rowMenuWidth}
-                                    selectedDocs={this._selectedDocs}
-                                    selectRow={this.selectRow}
-                                    startDrag={this.startDrag}
-                                    dragging={this._isDragging}
-                                    dropIndex={this.setDropIndex}
-                                    addRowRef={this.addRowRef}
-                                />
+                                <div className="schema-row-wrapper">
+                                    <DocumentView
+                                        {...this.props}
+                                        ref={r => (dref = r || undefined)}
+                                        LayoutTemplate={this.props.childLayoutTemplate}
+                                        LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
+                                        renderDepth={this.props.renderDepth + 1}
+                                        Document={doc}
+                                        DataDoc={dataDoc}
+                                        ContainingCollectionView={this.props.CollectionView}
+                                        ContainingCollectionDoc={this.Document}
+                                        PanelWidth={() => this.tableWidth}
+                                        PanelHeight={() => 70}
+                                        styleProvider={DefaultStyleProvider}
+                                        focus={this.focusDocument}
+                                        docFilters={this.childDocFilters}
+                                        docViewPath={this.props.docViewPath}
+                                        docRangeFilters={this.childDocRangeFilters}
+                                        searchFilterDocs={this.searchFilterDocs}
+                                        rootSelected={this.rootSelected}
+                                        ScreenToLocalTransform={() => this.getDocTransform(doc, dref)}
+                                        bringToFront={emptyFunction}
+                                        isContentActive={this.isChildContentActive}
+                                        hideDecorations={true}
+                                        hideTitle={true}
+                                        hideDocumentButtonBar={true}
+                                    />
+                                </div>
                             );
+                            // return (
+                            //     <SchemaRowBox
+                            //         {...this.props}
+                            //         key={index}
+                            //         Document={doc}
+                            //         ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                            //         ContainingCollectionView={this.props.CollectionView}
+                            //         rowIndex={index}
+                            //         columnKeys={this.columnKeys}
+                            //         columnWidths={this.displayColumnWidths}
+                            //         rowMenuWidth={CollectionSchemaView._rowMenuWidth}
+                            //         selectedDocs={this._selectedDocs}
+                            //         selectRow={this.selectRow}
+                            //         startDrag={this.startDrag}
+                            //         dragging={this._isDragging}
+                            //         dropIndex={this.setDropIndex}
+                            //         addRowRef={this.addRowRef}
+                            //     />
+                            // );
                         })}
                     </div>
                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 2cf0a1b79..0378fa67e 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,17 +1,17 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, ObservableSet } from 'mobx';
+import { computed, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../../../fields/Doc';
-import { DragManager } from '../../../util/DragManager';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
-import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
+import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
-import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
+import { Colors } from '../../global/globalEnums';
+import { DocCast, StrCast } from '../../../../fields/Types';
 
 export interface SchemaRowBoxProps extends FieldViewProps {
     rowIndex: number;
@@ -27,79 +27,93 @@ export interface SchemaRowBoxProps extends FieldViewProps {
 }
 
 @observer
-export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(SchemaRowBox, fieldKey);
+    }
+
     private _ref: HTMLDivElement | null = null;
 
     bounds = () => this._ref?.getBoundingClientRect();
 
-    isSelected = () => this.props.selectedDocs.has(this.props.Document);
+    @computed get schemaView() {
+        const vpath = this.props.docViewPath();
+        console.log(vpath[vpath.length - 2]);
+        return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as CollectionSchemaView) : undefined;
+    }
 
-    @action
-    onRowPointerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
+    @computed get schemaDoc() {
+        return this.props.ContainingCollectionDoc!;
+    }
 
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
-            emptyFunction,
-            e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
-        );
-    };
+    // isSelected = () => this.props.selectedDocs.has(this.props.Document);
 
-    onPointerEnter = (e: any) => {
-        if (!this.props.dragging) return;
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.addEventListener('pointermove', this.onPointerMove);
-    };
+    // @action
+    // onRowPointerDown = (e: React.PointerEvent) => {
+    //     e.stopPropagation();
 
-    onPointerMove = (e: any) => {
-        if (!this.props.dragging) return;
-        let dragIsRow: boolean = true;
-        DragManager.docsBeingDragged.forEach(doc => {
-            dragIsRow = this.props.selectedDocs.has(doc);
-        });
-        if (this._ref && dragIsRow) {
-            const rect = this._ref.getBoundingClientRect();
-            const y = e.clientY - rect.top; //y position within the element.
-            const height = this._ref.clientHeight;
-            const halfLine = height / 2;
-            if (y <= halfLine) {
-                this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                this._ref.style.borderBottom = '0px';
-                this.props.dropIndex(this.props.rowIndex);
-            } else if (y > halfLine) {
-                this._ref.style.borderTop = '0px';
-                this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                this.props.dropIndex(this.props.rowIndex + 1);
-            }
-        }
-    };
+    //     setupMoveUpEvents(
+    //         this,
+    //         e,
+    //         e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
+    //         emptyFunction,
+    //         e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
+    //     );
+    // };
+
+    // onPointerEnter = (e: any) => {
+    //     if (!this.props.dragging) return;
+    //     document.removeEventListener('pointermove', this.onPointerMove);
+    //     document.addEventListener('pointermove', this.onPointerMove);
+    // };
+
+    // onPointerMove = (e: any) => {
+    //     if (!this.props.dragging) return;
+    //     let dragIsRow: boolean = true;
+    //     DragManager.docsBeingDragged.forEach(doc => {
+    //         dragIsRow = this.props.selectedDocs.has(doc);
+    //     });
+    //     if (this._ref && dragIsRow) {
+    //         const rect = this._ref.getBoundingClientRect();
+    //         const y = e.clientY - rect.top; //y position within the element.
+    //         const height = this._ref.clientHeight;
+    //         const halfLine = height / 2;
+    //         if (y <= halfLine) {
+    //             this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
+    //             this._ref.style.borderBottom = '0px';
+    //             this.props.dropIndex(this.props.rowIndex);
+    //         } else if (y > halfLine) {
+    //             this._ref.style.borderTop = '0px';
+    //             this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
+    //             this.props.dropIndex(this.props.rowIndex + 1);
+    //         }
+    //     }
+    // };
 
     onPointerLeave = (e: any) => {
         if (this._ref) {
             this._ref.style.borderTop = '0px';
             this._ref.style.borderBottom = '0px';
         }
-        document.removeEventListener('pointermove', this.onPointerMove);
+        // document.removeEventListener('pointermove', this.onPointerMove);
     };
 
     render() {
         return (
             <div
                 className="schema-row"
-                style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}}
-                onPointerDown={this.onRowPointerDown}
-                onPointerEnter={this.onPointerEnter}
+                style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE /*, opacity: this.props.dragging ? 0.5 : 1 */ } : {}}
+                // onPointerDown={this.onRowPointerDown}
+                // onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    row && this.props.addRowRef(this.props.Document, row);
+                    // row && this.props.addRowRef(this.props.Document, row);
                     this._ref = row;
                 }}>
                 <div
                     className="row-menu"
                     style={{
-                        width: this.props.rowMenuWidth,
+                        width: CollectionSchemaView._rowMenuWidth,
                     }}>
                     <div
                         className="schema-row-button"
@@ -119,8 +133,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                     </div>
                 </div>
                 <div className="row-cells">
-                    {this.props.columnKeys.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+                    {this.schemaView?.columnKeys?.map((key, index) => (
+                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 5d9474173..e2f6d99f1 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,7 +1,14 @@
 import React = require('react');
 import { observer } from 'mobx-react';
-import { Doc, Field } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field } from '../../../../fields/Doc';
 import './CollectionSchemaView.scss';
+import { type } from 'jquery';
+import { action } from 'mobx';
+import { ComputedField } from '../../../../fields/ScriptField';
+import { FieldValue } from '../../../../fields/Types';
+import { CompileScript } from '../../../util/Scripting';
+import { EditableView } from '../../EditableView';
+import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
 
 export interface SchemaTableCellProps {
     Document: Doc;
@@ -14,7 +21,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     render() {
         return (
             <div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
-                {Field.toString(this.props.Document[this.props.fieldKey] as Field)}
+                {/* {Field.toString(this.props.Document[this.props.fieldKey] as Field)} */}
+                <EditableView contents={Field.toString(this.props.Document[this.props.fieldKey] as Field)} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string) => true} />
             </div>
         );
     }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 569579996..459554ebe 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -44,6 +44,7 @@ import { WebBox } from './WebBox';
 import React = require('react');
 import XRegExp = require('xregexp');
 import { LoadingBox } from './LoadingBox';
+import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -268,6 +269,7 @@ export class DocumentContentsView extends React.Component<
                     HTMLtag,
                     ComparisonBox,
                     LoadingBox,
+                    SchemaRowBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From d7d94fb4a9480a699eafa250a95821f7c584911d Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 28 Feb 2023 15:15:48 -0500
Subject: rows are documentviews (again)

---
 .../collections/collectionSchema/CollectionSchemaView.scss |  4 ++--
 .../collections/collectionSchema/CollectionSchemaView.tsx  | 14 ++++++++------
 .../views/collections/collectionSchema/SchemaRowBox.tsx    |  2 +-
 3 files changed, 11 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 46c2e2d1a..5c0b6d88b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -135,7 +135,7 @@
 }
 
 .schema-row-wrapper {
-    max-height: 70px;
+    // max-height: 70px;
     overflow: hidden;
 }
 
@@ -144,7 +144,7 @@
     display: flex;
     flex-direction: row;
     height: 100%;
-    max-height: 70px;
+    // max-height: 70px;
     overflow: auto;
 
     .schema-column-header,
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 379676f41..bd9303997 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -33,7 +33,7 @@ export enum ColumnType {
     Image,
 }
 
-const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data', 'creationDate'];
+const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate', 'links'];
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
@@ -43,6 +43,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _closestDropIndex: number = 0;
     private _previewRef: HTMLDivElement | null = null;
 
+    public static _rowHeight: number = 50;
     public static _minColWidth: number = 150;
     public static _rowMenuWidth: number = 100;
     public static _previewDividerWidth: number = 4;
@@ -419,7 +420,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
-        const newDoc = Docs.Create.TextDocument(value, { title: value });
+        const newDoc = Docs.Create.TextDocument(value, { title: value, _autoHeight: true });
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
         return this.props.addDocument?.(newDoc) || false;
@@ -534,7 +535,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                         SelectionManager.SelectSchemaViewDoc(undefined);
                         this._previewDoc = undefined;
                     })}>
-                    <div className="schema-header-row">
+                    <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
                         <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}></div>
                         {this.columnKeys.map((key, index) => {
                             return (
@@ -561,7 +562,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                             const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
                             let dref: Opt<DocumentView>;
                             return (
-                                <div className="schema-row-wrapper">
+                                <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
                                     <DocumentView
                                         {...this.props}
                                         ref={r => (dref = r || undefined)}
@@ -573,7 +574,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                         ContainingCollectionView={this.props.CollectionView}
                                         ContainingCollectionDoc={this.Document}
                                         PanelWidth={() => this.tableWidth}
-                                        PanelHeight={() => 70}
+                                        PanelHeight={() => CollectionSchemaView._rowHeight}
                                         styleProvider={DefaultStyleProvider}
                                         focus={this.focusDocument}
                                         docFilters={this.childDocFilters}
@@ -581,12 +582,13 @@ export class CollectionSchemaView extends CollectionSubView() {
                                         docRangeFilters={this.childDocRangeFilters}
                                         searchFilterDocs={this.searchFilterDocs}
                                         rootSelected={this.rootSelected}
-                                        ScreenToLocalTransform={() => this.getDocTransform(doc, dref)}
+                                        ScreenToLocalTransform={Transform.Identity}
                                         bringToFront={emptyFunction}
                                         isContentActive={this.isChildContentActive}
                                         hideDecorations={true}
                                         hideTitle={true}
                                         hideDocumentButtonBar={true}
+                                        fitWidth={returnTrue}
                                     />
                                 </div>
                             );
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 0378fa67e..e72ed3c4f 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -102,7 +102,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="schema-row"
-                style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE /*, opacity: this.props.dragging ? 0.5 : 1 */ } : {}}
+                style={this.props.isSelected() ? { height: CollectionSchemaView._rowHeight, backgroundColor: Colors.LIGHT_BLUE /*, opacity: this.props.dragging ? 0.5 : 1 */ } : { height: CollectionSchemaView._rowHeight }}
                 // onPointerDown={this.onRowPointerDown}
                 // onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
-- 
cgit v1.2.3-70-g09d2


From df4781b27848731b329068b13da89a17d04ad1a9 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 28 Feb 2023 17:13:11 -0500
Subject: selection working (except ctrl+click)

---
 .../collectionSchema/CollectionSchemaView.tsx      | 157 ++++++++++-----------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  56 ++++----
 .../collectionSchema/SchemaTableCell.tsx           |   4 +-
 3 files changed, 102 insertions(+), 115 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index bd9303997..22b8496aa 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -6,7 +6,7 @@ import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
@@ -24,6 +24,7 @@ import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
 import { DefaultStyleProvider } from '../../StyleProvider';
+import { DocumentManager } from '../../../util/DocumentManager';
 
 export enum ColumnType {
     Number,
@@ -38,7 +39,6 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate',
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
     private _ref: HTMLDivElement | null = null;
-    private _lastSelectedRow: number | undefined;
     private _selectedDocSortedArray: Doc[] = [];
     private _closestDropIndex: number = 0;
     private _previewRef: HTMLDivElement | null = null;
@@ -47,11 +47,11 @@ export class CollectionSchemaView extends CollectionSubView() {
     public static _minColWidth: number = 150;
     public static _rowMenuWidth: number = 100;
     public static _previewDividerWidth: number = 4;
+    @observable _lastSelectedRow: number | undefined;
     @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
     @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
     @observable _isDragging: boolean = false;
     @observable _displayColumnWidths: number[] | undefined;
-    @observable _previewDoc: Doc | undefined;
 
     get documentKeys() {
         const docs = this.childDocs;
@@ -110,6 +110,10 @@ export class CollectionSchemaView extends CollectionSubView() {
         return BoolCast(this.layoutDoc.sortDesc);
     }
 
+    rowIndex(doc: Doc) {
+        return this.childDocs.indexOf(doc);
+    }
+
     componentDidMount() {
         this.props.setContentView?.(this as DocComponentView);
         document.addEventListener('keydown', this.onKeyDown);
@@ -121,30 +125,32 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     onKeyDown = (e: KeyboardEvent) => {
-        if (this._selectedDocs.size > 0) {
-            if (e.key == 'ArrowDown') {
-                const lastDoc = Array.from(this._selectedDocs.values()).lastElement();
-                const lastIndex = this.childDocs.indexOf(lastDoc);
-                if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
-                    this._selectedDocs.clear();
-                    const newDoc = this.childDocs[lastIndex + 1];
-                    this._selectedDocs.add(newDoc);
-                    SelectionManager.SelectSchemaViewDoc(newDoc);
-                    this._previewDoc = newDoc;
-                }
-            }
-            if (e.key == 'ArrowUp') {
-                const firstDoc = Array.from(this._selectedDocs.values())[0];
-                const firstIndex = this.childDocs.indexOf(firstDoc);
-                if (firstIndex > 0) {
-                    this._selectedDocs.clear();
-                    const newDoc = this.childDocs[firstIndex - 1];
-                    this._selectedDocs.add(newDoc);
-                    SelectionManager.SelectSchemaViewDoc(newDoc);
-                    this._previewDoc = newDoc;
-                }
-            }
-        }
+        // if (this._selectedDocs.size > 0) {
+        //     if (e.key == 'ArrowDown') {
+        //         const lastDoc = Array.from(this._selectedDocs.values()).lastElement();
+        //         const lastIndex = this.childDocs.indexOf(lastDoc);
+        //         if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
+        //             this._selectedDocs.clear();
+        //             const newDoc = this.childDocs[lastIndex + 1];
+        //             this._selectedDocs.add(newDoc);
+        //             const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
+        //         if (presDocView) SelectionManager.SelectView(presDocView, false);
+        //             SelectionManager.SelectSchemaViewDoc(newDoc);
+        //             this._previewDoc = newDoc;
+        //         }
+        //     }
+        //     if (e.key == 'ArrowUp') {
+        //         const firstDoc = Array.from(this._selectedDocs.values())[0];
+        //         const firstIndex = this.childDocs.indexOf(firstDoc);
+        //         if (firstIndex > 0) {
+        //             this._selectedDocs.clear();
+        //             const newDoc = this.childDocs[firstIndex - 1];
+        //             this._selectedDocs.add(newDoc);
+        //             SelectionManager.SelectSchemaViewDoc(newDoc);
+        //             this._previewDoc = newDoc;
+        //         }
+        //     }
+        // }
     };
 
     @undoBatch
@@ -304,40 +310,51 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    selectRow = (e: React.PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
+    addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
+        this._selectedDocs.add(doc);
+        const rowDocView = DocumentManager.Instance.getDocumentView(doc);
+        if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection);
+        this._lastSelectedRow = index;
+    };
+
+    @action
+    removeDocFromSelection = (doc: Doc) => {
+        if (this._selectedDocs.has(doc)) this._selectedDocs.delete(doc);
+        const rowDocView = DocumentManager.Instance.getDocumentView(doc);
+        if (rowDocView) SelectionManager.DeselectView(rowDocView);
+    };
+
+    @action
+    clearSelection = () => {
+        this._selectedDocs.clear();
+        SelectionManager.DeselectAll();
+        this._lastSelectedRow = undefined;
+    };
+
+    @action
+    selectRow = (e: PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
+        e.stopPropagation();
+        if (index < 0) return;
         const ctrl = e.ctrlKey || e.metaKey;
         const shift = e.shiftKey;
         if (shift && this._lastSelectedRow !== undefined) {
             const startRow = Math.min(this._lastSelectedRow, index);
             const endRow = Math.max(this._lastSelectedRow, index);
             for (let i = startRow; i <= endRow; i++) {
-                const currDoc: Doc = this.childDocs[i];
-                if (!this._selectedDocs.has(currDoc)) this._selectedDocs.add(currDoc);
+                this.addDocToSelection(this.childDocs[i], true, i);
             }
-            this._lastSelectedRow = endRow;
+            this._lastSelectedRow = index;
         } else if (ctrl) {
             if (!this._selectedDocs.has(doc)) {
-                this._selectedDocs.add(doc);
-                this._lastSelectedRow = index;
+                console.log('add');
+                this.addDocToSelection(doc, true, index);
             } else {
-                this._selectedDocs.delete(doc);
+                console.log('remove');
+                this.removeDocFromSelection(doc);
             }
         } else {
-            this._selectedDocs.clear();
-            this._selectedDocs.add(doc);
-            this._lastSelectedRow = index;
-        }
-
-        if (this._lastSelectedRow && this._selectedDocs.size > 0) {
-            SelectionManager.SelectSchemaViewDoc(this.childDocs[this._lastSelectedRow]);
-        } else {
-            SelectionManager.SelectSchemaViewDoc(undefined);
-        }
-
-        if (this._selectedDocs.size == 1) {
-            this._previewDoc = Array.from(this._selectedDocs)[0];
-        } else {
-            this._previewDoc = undefined;
+            this.clearSelection();
+            this.addDocToSelection(doc, false, index);
         }
     };
 
@@ -381,10 +398,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     startDrag = (e: React.PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
         if (!this._selectedDocs.has(doc)) {
-            this._selectedDocs.clear();
-            this._selectedDocs.add(doc);
-            this._lastSelectedRow = index;
-            SelectionManager.SelectSchemaViewDoc(doc);
+            this.clearSelection();
+            this.addDocToSelection(doc, false, index);
         }
         this._isDragging = true;
         this._selectedDocSortedArray = this.sortedSelectedDocs();
@@ -511,13 +526,9 @@ export class CollectionSchemaView extends CollectionSubView() {
             afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
         });
     };
+
     isChildContentActive = () =>
         this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
-    getDocTransform(doc: Doc, dref?: DocumentView) {
-        const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
-        // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
-        return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
-    }
 
     render() {
         return (
@@ -531,9 +542,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <div
                     className="schema-table"
                     onPointerDown={action(() => {
-                        this._selectedDocs.clear();
-                        SelectionManager.SelectSchemaViewDoc(undefined);
-                        this._previewDoc = undefined;
+                        this.clearSelection();
                     })}>
                     <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
                         <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}></div>
@@ -552,7 +561,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                                     addColumn={this.addColumn}
                                     removeColumn={this.removeColumn}
                                     resizeColumn={this.startResize}
-                                    // dragColumn={this.dragColumn}
                                 />
                             );
                         })}
@@ -568,7 +576,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                                         ref={r => (dref = r || undefined)}
                                         LayoutTemplate={this.props.childLayoutTemplate}
                                         LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
-                                        renderDepth={this.props.renderDepth + 1}
                                         Document={doc}
                                         DataDoc={dataDoc}
                                         ContainingCollectionView={this.props.CollectionView}
@@ -578,7 +585,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                                         styleProvider={DefaultStyleProvider}
                                         focus={this.focusDocument}
                                         docFilters={this.childDocFilters}
-                                        docViewPath={this.props.docViewPath}
                                         docRangeFilters={this.childDocRangeFilters}
                                         searchFilterDocs={this.searchFilterDocs}
                                         rootSelected={this.rootSelected}
@@ -592,25 +598,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                                     />
                                 </div>
                             );
-                            // return (
-                            //     <SchemaRowBox
-                            //         {...this.props}
-                            //         key={index}
-                            //         Document={doc}
-                            //         ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                            //         ContainingCollectionView={this.props.CollectionView}
-                            //         rowIndex={index}
-                            //         columnKeys={this.columnKeys}
-                            //         columnWidths={this.displayColumnWidths}
-                            //         rowMenuWidth={CollectionSchemaView._rowMenuWidth}
-                            //         selectedDocs={this._selectedDocs}
-                            //         selectRow={this.selectRow}
-                            //         startDrag={this.startDrag}
-                            //         dragging={this._isDragging}
-                            //         dropIndex={this.setDropIndex}
-                            //         addRowRef={this.addRowRef}
-                            //     />
-                            // );
                         })}
                     </div>
                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
@@ -618,9 +605,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                 {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
                 {this.previewWidth > 0 && (
                     <div style={{ width: `${this.previewWidth}px` }} ref={ref => (this._previewRef = ref)}>
-                        {this._previewDoc && (
+                        {this._lastSelectedRow !== undefined && (
                             <DocumentView
-                                Document={this._previewDoc}
+                                Document={this.childDocs[this._lastSelectedRow]}
                                 DataDoc={undefined}
                                 fitContentsToBox={returnTrue}
                                 dontCenter={'y'}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index e72ed3c4f..d67e6f2d7 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,6 +1,6 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed, ObservableSet } from 'mobx';
+import { action, computed, ObservableSet } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../../../fields/Doc';
 import { undoBatch } from '../../../util/UndoManager';
@@ -12,19 +12,7 @@ import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
 import { Colors } from '../../global/globalEnums';
 import { DocCast, StrCast } from '../../../../fields/Types';
-
-export interface SchemaRowBoxProps extends FieldViewProps {
-    rowIndex: number;
-    columnKeys: string[];
-    columnWidths: number[];
-    rowMenuWidth: number;
-    selectedDocs: ObservableSet<Doc>;
-    selectRow: (e: any, doc: Doc, ref: HTMLDivElement, index: number) => void;
-    startDrag: (e: any, doc: Doc, ref: HTMLDivElement, index: number) => boolean;
-    dragging: boolean;
-    dropIndex: (index: number) => void;
-    addRowRef: (doc: Doc, ref: HTMLDivElement) => void;
-}
+import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -38,7 +26,6 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get schemaView() {
         const vpath = this.props.docViewPath();
-        console.log(vpath[vpath.length - 2]);
         return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as CollectionSchemaView) : undefined;
     }
 
@@ -46,20 +33,25 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.props.ContainingCollectionDoc!;
     }
 
+    @computed get rowIndex() {
+        return this.schemaView?.rowIndex(this.rootDoc) ?? -1;
+    }
+
     // isSelected = () => this.props.selectedDocs.has(this.props.Document);
 
-    // @action
-    // onRowPointerDown = (e: React.PointerEvent) => {
-    //     e.stopPropagation();
+    @action
+    onRowPointerDown = (e: React.PointerEvent) => {
+        e.stopPropagation();
 
-    //     setupMoveUpEvents(
-    //         this,
-    //         e,
-    //         e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex),
-    //         emptyFunction,
-    //         e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex)
-    //     );
-    // };
+        setupMoveUpEvents(
+            this,
+            e,
+            // e => this.schemaView?.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex) ?? true,
+            returnTrue,
+            emptyFunction,
+            e => this.schemaView?.selectRow(e, this.props.Document, this._ref!, this.rowIndex)
+        );
+    };
 
     // onPointerEnter = (e: any) => {
     //     if (!this.props.dragging) return;
@@ -102,8 +94,12 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="schema-row"
-                style={this.props.isSelected() ? { height: CollectionSchemaView._rowHeight, backgroundColor: Colors.LIGHT_BLUE /*, opacity: this.props.dragging ? 0.5 : 1 */ } : { height: CollectionSchemaView._rowHeight }}
-                // onPointerDown={this.onRowPointerDown}
+                style={
+                    this.props.isSelected()
+                        ? { height: CollectionSchemaView._rowHeight, backgroundColor: Colors.LIGHT_BLUE, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined /*, opacity: this.props.dragging ? 0.5 : 1 */ }
+                        : { height: CollectionSchemaView._rowHeight, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }
+                }
+                onPointerDown={this.onRowPointerDown}
                 // onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
@@ -141,3 +137,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
     }
 }
+function // e => this.schemaView?.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex) ?? true,
+returnTrue(e: PointerEvent, down: number[], delta: number[]): boolean {
+    throw new Error('Function not implemented.');
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index e2f6d99f1..4cfc5850c 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -21,8 +21,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     render() {
         return (
             <div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
-                {/* {Field.toString(this.props.Document[this.props.fieldKey] as Field)} */}
-                <EditableView contents={Field.toString(this.props.Document[this.props.fieldKey] as Field)} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string) => true} />
+                {Field.toString(this.props.Document[this.props.fieldKey] as Field)}
+                {/* <EditableView contents={Field.toString(this.props.Document[this.props.fieldKey] as Field)} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string) => true} /> */}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From d7cb4929e437f2a03336e21b16aa4fb0cebf5ebe Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 28 Feb 2023 17:20:17 -0500
Subject: added dragging code

---
 .../collectionSchema/CollectionSchemaView.tsx      |  2 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  | 70 ++++++++++------------
 2 files changed, 34 insertions(+), 38 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 22b8496aa..0cf5a1ed9 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -396,7 +396,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    startDrag = (e: React.PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
+    startDrag = (e: PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
         if (!this._selectedDocs.has(doc)) {
             this.clearSelection();
             this.addDocToSelection(doc, false, index);
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index d67e6f2d7..0e7b0fa16 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -13,6 +13,7 @@ import { SchemaTableCell } from './SchemaTableCell';
 import { Colors } from '../../global/globalEnums';
 import { DocCast, StrCast } from '../../../../fields/Types';
 import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
+import { DragManager } from '../../../util/DragManager';
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -46,48 +47,47 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         setupMoveUpEvents(
             this,
             e,
-            // e => this.schemaView?.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex) ?? true,
-            returnTrue,
+            e => this.schemaView?.startDrag(e, this.rootDoc, this._ref!, this.rowIndex) ?? true,
             emptyFunction,
-            e => this.schemaView?.selectRow(e, this.props.Document, this._ref!, this.rowIndex)
+            e => this.schemaView?.selectRow(e, this.rootDoc, this._ref!, this.rowIndex)
         );
     };
 
-    // onPointerEnter = (e: any) => {
-    //     if (!this.props.dragging) return;
-    //     document.removeEventListener('pointermove', this.onPointerMove);
-    //     document.addEventListener('pointermove', this.onPointerMove);
-    // };
+    onPointerEnter = (e: any) => {
+        if (!this.schemaView?._isDragging) return;
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.addEventListener('pointermove', this.onPointerMove);
+    };
 
-    // onPointerMove = (e: any) => {
-    //     if (!this.props.dragging) return;
-    //     let dragIsRow: boolean = true;
-    //     DragManager.docsBeingDragged.forEach(doc => {
-    //         dragIsRow = this.props.selectedDocs.has(doc);
-    //     });
-    //     if (this._ref && dragIsRow) {
-    //         const rect = this._ref.getBoundingClientRect();
-    //         const y = e.clientY - rect.top; //y position within the element.
-    //         const height = this._ref.clientHeight;
-    //         const halfLine = height / 2;
-    //         if (y <= halfLine) {
-    //             this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
-    //             this._ref.style.borderBottom = '0px';
-    //             this.props.dropIndex(this.props.rowIndex);
-    //         } else if (y > halfLine) {
-    //             this._ref.style.borderTop = '0px';
-    //             this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-    //             this.props.dropIndex(this.props.rowIndex + 1);
-    //         }
-    //     }
-    // };
+    onPointerMove = (e: any) => {
+        if (!this.schemaView?._isDragging) return;
+        let dragIsRow: boolean = true;
+        DragManager.docsBeingDragged.forEach(doc => {
+            dragIsRow = this.schemaView?._selectedDocs.has(doc) ?? false;
+        });
+        if (this._ref && dragIsRow) {
+            const rect = this._ref.getBoundingClientRect();
+            const y = e.clientY - rect.top; //y position within the element.
+            const height = this._ref.clientHeight;
+            const halfLine = height / 2;
+            if (y <= halfLine) {
+                this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
+                this._ref.style.borderBottom = '0px';
+                this.schemaView?.setDropIndex(this.rowIndex);
+            } else if (y > halfLine) {
+                this._ref.style.borderTop = '0px';
+                this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
+                this.schemaView?.setDropIndex(this.rowIndex + 1);
+            }
+        }
+    };
 
     onPointerLeave = (e: any) => {
         if (this._ref) {
             this._ref.style.borderTop = '0px';
             this._ref.style.borderBottom = '0px';
         }
-        // document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointermove', this.onPointerMove);
     };
 
     render() {
@@ -100,10 +100,10 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         : { height: CollectionSchemaView._rowHeight, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }
                 }
                 onPointerDown={this.onRowPointerDown}
-                // onPointerEnter={this.onPointerEnter}
+                onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    // row && this.props.addRowRef(this.props.Document, row);
+                    row && this.schemaView?.addRowRef(this.rootDoc, row);
                     this._ref = row;
                 }}>
                 <div
@@ -137,7 +137,3 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
     }
 }
-function // e => this.schemaView?.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex) ?? true,
-returnTrue(e: PointerEvent, down: number[], delta: number[]): boolean {
-    throw new Error('Function not implemented.');
-}
-- 
cgit v1.2.3-70-g09d2


From 027c89584d087ada9d45a46cb620b157ae29d0b9 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 28 Feb 2023 17:44:16 -0500
Subject: arrow key movement

---
 .../collectionSchema/CollectionSchemaView.tsx      | 46 ++++++++++------------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  2 -
 2 files changed, 20 insertions(+), 28 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 0cf5a1ed9..a0e1ae8b2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -125,32 +125,26 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     onKeyDown = (e: KeyboardEvent) => {
-        // if (this._selectedDocs.size > 0) {
-        //     if (e.key == 'ArrowDown') {
-        //         const lastDoc = Array.from(this._selectedDocs.values()).lastElement();
-        //         const lastIndex = this.childDocs.indexOf(lastDoc);
-        //         if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
-        //             this._selectedDocs.clear();
-        //             const newDoc = this.childDocs[lastIndex + 1];
-        //             this._selectedDocs.add(newDoc);
-        //             const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
-        //         if (presDocView) SelectionManager.SelectView(presDocView, false);
-        //             SelectionManager.SelectSchemaViewDoc(newDoc);
-        //             this._previewDoc = newDoc;
-        //         }
-        //     }
-        //     if (e.key == 'ArrowUp') {
-        //         const firstDoc = Array.from(this._selectedDocs.values())[0];
-        //         const firstIndex = this.childDocs.indexOf(firstDoc);
-        //         if (firstIndex > 0) {
-        //             this._selectedDocs.clear();
-        //             const newDoc = this.childDocs[firstIndex - 1];
-        //             this._selectedDocs.add(newDoc);
-        //             SelectionManager.SelectSchemaViewDoc(newDoc);
-        //             this._previewDoc = newDoc;
-        //         }
-        //     }
-        // }
+        if (this._selectedDocs.size > 0) {
+            if (e.key == 'ArrowDown') {
+                const lastDoc = Array.from(this._selectedDocs.values()).lastElement();
+                const lastIndex = this.rowIndex(lastDoc);
+                if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
+                    !e.shiftKey && this.clearSelection();
+                    const newDoc = this.childDocs[lastIndex + 1];
+                    this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
+                }
+            }
+            if (e.key == 'ArrowUp') {
+                const firstDoc = Array.from(this._selectedDocs.values())[0];
+                const firstIndex = this.rowIndex(firstDoc);
+                if (firstIndex > 0 && firstIndex < this.childDocs.length) {
+                    !e.shiftKey && this.clearSelection();
+                    const newDoc = this.childDocs[firstIndex - 1];
+                    this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
+                }
+            }
+        }
     };
 
     @undoBatch
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 0e7b0fa16..0ed5f2df4 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -38,8 +38,6 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.schemaView?.rowIndex(this.rootDoc) ?? -1;
     }
 
-    // isSelected = () => this.props.selectedDocs.has(this.props.Document);
-
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
-- 
cgit v1.2.3-70-g09d2


From 4137fa5fedae84aa781f3ba22ddfb2410a0cad9a Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 28 Feb 2023 21:15:37 -0500
Subject: preserve sort when adding docs, break sort when rearranging

---
 .../collectionSchema/CollectionSchemaView.tsx      | 45 ++++++++++++++--------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  6 +--
 2 files changed, 33 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index a0e1ae8b2..1a26f1178 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -8,7 +8,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTransparent, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -69,7 +69,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get previewWidth() {
-        return NumCast(this.props.Document.schemaPreviewWidth);
+        return NumCast(this.layoutDoc.schemaPreviewWidth);
     }
 
     @computed get tableWidth() {
@@ -103,7 +103,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get sortField() {
-        return StrCast(this.layoutDoc.sortField, 'creationDate');
+        return StrCast(this.layoutDoc.sortField);
     }
 
     @computed get sortDesc() {
@@ -149,10 +149,14 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @undoBatch
     @action
-    setSort = (field: string, desc: boolean) => {
+    setSort = (field: string | undefined, desc: boolean = false) => {
         this.layoutDoc.sortField = field;
         this.layoutDoc.sortDesc = desc;
 
+        console.log(field, desc);
+
+        if (field === undefined) return;
+
         this.childDocs.sort((docA, docB) => {
             const aStr = Field.toString(docA[field] as Field);
             const bStr = Field.toString(docB[field] as Field);
@@ -164,6 +168,12 @@ export class CollectionSchemaView extends CollectionSubView() {
         });
     };
 
+    addRow = (doc: Doc | Doc[]) => {
+        const result: boolean = this.addDocument(doc);
+        this.setSort(this.sortField, this.sortDesc);
+        return result;
+    };
+
     @undoBatch
     @action
     changeColumnKey = (index: number, newKey: string, defaultVal?: any) => {
@@ -316,6 +326,9 @@ export class CollectionSchemaView extends CollectionSubView() {
         if (this._selectedDocs.has(doc)) this._selectedDocs.delete(doc);
         const rowDocView = DocumentManager.Instance.getDocumentView(doc);
         if (rowDocView) SelectionManager.DeselectView(rowDocView);
+        if (this._selectedDocs.size === 0) {
+            this._lastSelectedRow = undefined;
+        }
     };
 
     @action
@@ -340,10 +353,8 @@ export class CollectionSchemaView extends CollectionSubView() {
             this._lastSelectedRow = index;
         } else if (ctrl) {
             if (!this._selectedDocs.has(doc)) {
-                console.log('add');
                 this.addDocToSelection(doc, true, index);
             } else {
-                console.log('remove');
                 this.removeDocFromSelection(doc);
             }
         } else {
@@ -370,6 +381,7 @@ export class CollectionSchemaView extends CollectionSubView() {
             this.props.removeDocument?.(this._selectedDocSortedArray);
             this.addDocument(this._selectedDocSortedArray);
             this.addDocument(pushedDocs);
+            this.setSort(undefined);
             return true;
         }
         return false;
@@ -383,10 +395,13 @@ export class CollectionSchemaView extends CollectionSubView() {
             undoBatch(
                 action(docus => {
                     this._isDragging = false;
-                    docus.map((doc: Doc) => this.addDocument(doc));
+                    docus.map((doc: Doc) => {
+                        this.addDocument(doc);
+                    });
                 })
             )
         );
+        this.setSort(undefined);
     };
 
     @action
@@ -422,7 +437,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         const maxWidth = 1000;
         const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
         const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
-        this.props.Document.schemaPreviewWidth = width;
+        this.layoutDoc.schemaPreviewWidth = width;
         return false;
     };
 
@@ -432,7 +447,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         const newDoc = Docs.Create.TextDocument(value, { title: value, _autoHeight: true });
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
-        return this.props.addDocument?.(newDoc) || false;
+        return this.addRow(newDoc) || false;
     };
 
     menuCallback = (x: number, y: number) => {
@@ -444,9 +459,9 @@ export class CollectionSchemaView extends CollectionSubView() {
         DocUtils.addDocumentCreatorMenuItems(
             doc => {
                 FormattedTextBox.SelectOnLoad = StrCast(doc[Id]);
-                return this.addDocument(doc);
+                return this.addRow(doc);
             },
-            this.addDocument,
+            this.addRow,
             x,
             y,
             true
@@ -463,7 +478,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                             if (this.props.Document.isTemplateDoc) {
                                 Doc.MakeMetadataFieldTemplate(created, this.props.Document);
                             }
-                            return this.props.addDocument?.(created);
+                            return this.addRow(created);
                         }
                     },
                     icon: 'compress-arrows-alt',
@@ -482,7 +497,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 Doc.MakeMetadataFieldTemplate(created, container);
                                 return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
                             }
-                            return this.props.addDocument?.(created) || false;
+                            return this.addRow(created) || false;
                         }
                     },
                     icon: 'compress-arrows-alt',
@@ -497,7 +512,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 if (this.props.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.Document);
                 }
-                this.props.addDocument?.(created);
+                this.addRow(created);
             }
         });
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
@@ -621,7 +636,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                                 ContainingCollectionView={this.props.CollectionView}
                                 moveDocument={this.props.moveDocument}
-                                addDocument={this.props.addDocument}
+                                addDocument={this.addRow}
                                 removeDocument={this.props.removeDocument}
                                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                                 addDocTab={this.props.addDocTab}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 0ed5f2df4..d6a00966d 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -113,7 +113,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         className="schema-row-button"
                         onPointerDown={undoBatch(e => {
                             e.stopPropagation();
-                            this.props.removeDocument?.(this.props.Document);
+                            this.props.removeDocument?.(this.rootDoc);
                         })}>
                         <FontAwesomeIcon icon="times" />
                     </div>
@@ -121,14 +121,14 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         className="schema-row-button"
                         onPointerDown={e => {
                             e.stopPropagation();
-                            this.props.addDocTab(this.props.Document, OpenWhere.addRight);
+                            this.props.addDocTab(this.rootDoc, OpenWhere.addRight);
                         }}>
                         <FontAwesomeIcon icon="external-link-alt" />
                     </div>
                 </div>
                 <div className="row-cells">
                     {this.schemaView?.columnKeys?.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} />
+                        <SchemaTableCell key={key} Document={this.rootDoc} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} />
                     ))}
                 </div>
             </div>
-- 
cgit v1.2.3-70-g09d2


From 9853c1e3ca1c3a1d2da210118b927adc559e67fa Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Wed, 1 Mar 2023 17:53:56 -0500
Subject: override onclick script

---
 .../collectionSchema/CollectionSchemaView.tsx        | 20 +++++++++++++-------
 .../collections/collectionSchema/SchemaRowBox.tsx    | 20 ++++++++++----------
 src/client/views/nodes/DocumentView.tsx              |  3 ++-
 3 files changed, 25 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 1a26f1178..02591489b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -25,6 +25,7 @@ import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
 import { DefaultStyleProvider } from '../../StyleProvider';
 import { DocumentManager } from '../../../util/DocumentManager';
+import { ScriptField } from '../../../../fields/ScriptField';
 
 export enum ColumnType {
     Number,
@@ -338,12 +339,13 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._lastSelectedRow = undefined;
     };
 
+    rowOnClickScript = ScriptField.MakeFunction('scriptContext.selectRow(self, shiftKey, ctrlKey || metaKey)', { scriptContext: 'any', shiftKey: 'boolean', ctrlKey: 'boolean', metaKey: 'boolean' })!;
+
     @action
-    selectRow = (e: PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
-        e.stopPropagation();
+    selectRow = (doc: Doc, shift: boolean, ctrl: boolean) => {
+        console.log(ctrl);
+        const index = this.childDocs.indexOf(doc);
         if (index < 0) return;
-        const ctrl = e.ctrlKey || e.metaKey;
-        const shift = e.shiftKey;
         if (shift && this._lastSelectedRow !== undefined) {
             const startRow = Math.min(this._lastSelectedRow, index);
             const endRow = Math.max(this._lastSelectedRow, index);
@@ -358,8 +360,10 @@ export class CollectionSchemaView extends CollectionSubView() {
                 this.removeDocFromSelection(doc);
             }
         } else {
-            this.clearSelection();
-            this.addDocToSelection(doc, false, index);
+            if (!this._selectedDocs.has(doc)) {
+                this.clearSelection();
+                this.addDocToSelection(doc, false, index);
+            }
         }
     };
 
@@ -405,7 +409,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    startDrag = (e: PointerEvent, doc: Doc, ref: HTMLDivElement, index: number) => {
+    startDrag = (e: PointerEvent, doc: Doc, index: number) => {
         if (!this._selectedDocs.has(doc)) {
             this.clearSelection();
             this.addDocToSelection(doc, false, index);
@@ -604,6 +608,8 @@ export class CollectionSchemaView extends CollectionSubView() {
                                         hideTitle={true}
                                         hideDocumentButtonBar={true}
                                         fitWidth={returnTrue}
+                                        onClick={() => this.rowOnClickScript}
+                                        scriptContext={this}
                                     />
                                 </div>
                             );
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index d6a00966d..5de9fdf5c 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -42,13 +42,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     onRowPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
 
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.schemaView?.startDrag(e, this.rootDoc, this._ref!, this.rowIndex) ?? true,
-            emptyFunction,
-            e => this.schemaView?.selectRow(e, this.rootDoc, this._ref!, this.rowIndex)
-        );
+        // setupMoveUpEvents(
+        //     this,
+        //     e,
+        //     e => this.schemaView?.startDrag(e, this.rootDoc, this.rowIndex) ?? true,
+        //     emptyFunction,
+        //     e => this.schemaView?.selectRow(e, this.rootDoc, this.rowIndex)
+        // );
     };
 
     onPointerEnter = (e: any) => {
@@ -97,9 +97,9 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         ? { height: CollectionSchemaView._rowHeight, backgroundColor: Colors.LIGHT_BLUE, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined /*, opacity: this.props.dragging ? 0.5 : 1 */ }
                         : { height: CollectionSchemaView._rowHeight, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }
                 }
-                onPointerDown={this.onRowPointerDown}
-                onPointerEnter={this.onPointerEnter}
-                onPointerLeave={this.onPointerLeave}
+                // onPointerDown={this.onRowPointerDown}
+                // onPointerEnter={this.onPointerEnter}
+                // onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
                     row && this.schemaView?.addRowRef(this.rootDoc, row);
                     this._ref = row;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 36c0240f1..5e7037754 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -644,7 +644,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 }
             } else if (!this._longPress && this.onClickHandler?.script && !isScriptBox()) {
                 // bcz: hack? don't execute script if you're clicking on a scripting box itself
-                const { clientX, clientY, shiftKey, altKey } = e;
+                const { clientX, clientY, shiftKey, altKey, metaKey } = e;
                 const func = () =>
                     this.onClickHandler.script.run(
                         {
@@ -658,6 +658,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             clientY,
                             shiftKey,
                             altKey,
+                            metaKey,
                         },
                         console.log
                     ).result?.select === true
-- 
cgit v1.2.3-70-g09d2


From fdf2f866e948d030e7b29009ed5c06e384d74fe1 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Wed, 1 Mar 2023 21:17:28 -0500
Subject: drag and drop working

---
 .../collectionSchema/CollectionSchemaView.tsx            |  4 ++--
 .../views/collections/collectionSchema/SchemaRowBox.tsx  | 16 ++++------------
 2 files changed, 6 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 02591489b..c52a7f3e4 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -343,14 +343,14 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     selectRow = (doc: Doc, shift: boolean, ctrl: boolean) => {
-        console.log(ctrl);
         const index = this.childDocs.indexOf(doc);
         if (index < 0) return;
         if (shift && this._lastSelectedRow !== undefined) {
             const startRow = Math.min(this._lastSelectedRow, index);
             const endRow = Math.max(this._lastSelectedRow, index);
             for (let i = startRow; i <= endRow; i++) {
-                this.addDocToSelection(this.childDocs[i], true, i);
+                const currDoc = this.childDocs[i];
+                if (!this._selectedDocs.has(currDoc)) this.addDocToSelection(currDoc, true, i);
             }
             this._lastSelectedRow = index;
         } else if (ctrl) {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 5de9fdf5c..4b709f670 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -40,15 +40,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
-        e.stopPropagation();
-
-        // setupMoveUpEvents(
-        //     this,
-        //     e,
-        //     e => this.schemaView?.startDrag(e, this.rootDoc, this.rowIndex) ?? true,
-        //     emptyFunction,
-        //     e => this.schemaView?.selectRow(e, this.rootDoc, this.rowIndex)
-        // );
+        setupMoveUpEvents(this, e, e => this.schemaView?.startDrag(e, this.rootDoc, this.rowIndex) ?? true, emptyFunction, emptyFunction, false);
     };
 
     onPointerEnter = (e: any) => {
@@ -97,9 +89,9 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         ? { height: CollectionSchemaView._rowHeight, backgroundColor: Colors.LIGHT_BLUE, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined /*, opacity: this.props.dragging ? 0.5 : 1 */ }
                         : { height: CollectionSchemaView._rowHeight, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }
                 }
-                // onPointerDown={this.onRowPointerDown}
-                // onPointerEnter={this.onPointerEnter}
-                // onPointerLeave={this.onPointerLeave}
+                onPointerDown={this.onRowPointerDown}
+                onPointerEnter={this.onPointerEnter}
+                onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
                     row && this.schemaView?.addRowRef(this.rootDoc, row);
                     this._ref = row;
-- 
cgit v1.2.3-70-g09d2


From 67a0081bb4fb713b730fa86a5b67d3c061426514 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Wed, 1 Mar 2023 21:45:13 -0500
Subject: cells editable

---
 .../collections/collectionSchema/SchemaRowBox.tsx  |  4 +-
 .../collectionSchema/SchemaTableCell.tsx           | 45 +++++++++++++++++++++-
 2 files changed, 45 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 4b709f670..05197d05f 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -93,7 +93,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
-                    row && this.schemaView?.addRowRef(this.rootDoc, row);
+                    row && this.schemaView?.addRowRef?.(this.rootDoc, row);
                     this._ref = row;
                 }}>
                 <div
@@ -120,7 +120,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
                 <div className="row-cells">
                     {this.schemaView?.columnKeys?.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.rootDoc} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} />
+                        <SchemaTableCell key={key} Document={this.rootDoc} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} isRowActive={this.props.isContentActive} />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 4cfc5850c..0f832e7c4 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -9,20 +9,61 @@ import { FieldValue } from '../../../../fields/Types';
 import { CompileScript } from '../../../util/Scripting';
 import { EditableView } from '../../EditableView';
 import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
+import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { KeyValueBox } from '../../nodes/KeyValueBox';
+import { returnEmptyFilter, returnEmptyDoclist, returnFalse, emptyFunction, returnZero } from '../../../../Utils';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { Transform } from '../../../util/Transform';
+import { CollectionSchemaView } from './CollectionSchemaView';
 
 export interface SchemaTableCellProps {
     Document: Doc;
     fieldKey: string;
     columnWidth: number;
+    isRowActive: () => boolean | undefined;
 }
 
 @observer
 export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     render() {
+        const props: FieldViewProps = {
+            Document: this.props.Document,
+            // DataDoc: this.props.doc,
+            docFilters: returnEmptyFilter,
+            docRangeFilters: returnEmptyFilter,
+            searchFilterDocs: returnEmptyDoclist,
+            styleProvider: DefaultStyleProvider,
+            docViewPath: returnEmptyDoclist,
+            ContainingCollectionView: undefined,
+            ContainingCollectionDoc: undefined,
+            fieldKey: this.props.fieldKey,
+            rootSelected: returnFalse,
+            isSelected: returnFalse,
+            setHeight: returnFalse,
+            select: emptyFunction,
+            dropAction: 'alias',
+            bringToFront: emptyFunction,
+            renderDepth: 1,
+            isContentActive: returnFalse,
+            whenChildContentsActiveChanged: emptyFunction,
+            ScreenToLocalTransform: Transform.Identity,
+            focus: emptyFunction,
+            PanelWidth: () => this.props.columnWidth,
+            PanelHeight: () => CollectionSchemaView._rowHeight,
+            addDocTab: returnFalse,
+            pinToPres: returnZero,
+        };
+
         return (
-            <div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
-                {Field.toString(this.props.Document[this.props.fieldKey] as Field)}
+            <div className="schema-table-cell" style={{ width: this.props.columnWidth, pointerEvents: this.props.isRowActive() ? 'all' : 'none' }}>
+                {/* {Field.toString(this.props.Document[this.props.fieldKey] as Field)} */}
                 {/* <EditableView contents={Field.toString(this.props.Document[this.props.fieldKey] as Field)} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string) => true} /> */}
+                <EditableView
+                    contents={<FieldView {...props} />}
+                    height={'auto'}
+                    GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
+                    SetValue={(value: string) => KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value)}
+                />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 3fed87fe05a70a5ef63ed7989c7a28faee68bf4b Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Wed, 22 Mar 2023 17:42:28 -0400
Subject: shift enter shortcut to fill column

---
 .../views/collections/collectionSchema/CollectionSchemaView.scss | 7 +++----
 .../views/collections/collectionSchema/CollectionSchemaView.tsx  | 7 +++++++
 src/client/views/collections/collectionSchema/SchemaRowBox.tsx   | 9 ++++++++-
 .../views/collections/collectionSchema/SchemaTableCell.tsx       | 8 +++++++-
 4 files changed, 25 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 1853fb589..287e2b01b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -109,6 +109,8 @@
     align-items: center;
     padding: 0;
     z-index: 1;
+    border: 1px solid $medium-gray;
+    overflow: hidden;
 
     .schema-column-title {
         flex-grow: 2;
@@ -146,7 +148,6 @@
 }
 
 .schema-row-wrapper {
-    // max-height: 70px;
     overflow: hidden;
 }
 
@@ -159,16 +160,14 @@
     display: flex;
     flex-direction: row;
     height: 100%;
-    // max-height: 70px;
     overflow: auto;
 }
 
-.schema-column-header,
 .schema-table-cell,
 .row-menu {
     border: 1px solid $medium-gray;
-    padding: 5px;
     overflow: hidden;
+    padding: 5px;
 }
 
 .schema-row {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 4498a2258..48db1467c 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -28,6 +28,7 @@ import { DocumentManager } from '../../../util/DocumentManager';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { IconButton } from 'browndash-components';
+import { KeyValueBox } from '../../nodes/KeyValueBox';
 
 export enum ColumnType {
     Number,
@@ -607,6 +608,12 @@ export class CollectionSchemaView extends CollectionSubView() {
         this.closeColumnMenu();
     };
 
+    setColumnValues = (key: string, value: string) => {
+        let success: boolean = true;
+        this.childDocs.forEach(doc => success && KeyValueBox.SetField(doc, key, value));
+        return success;
+    };
+
     @action
     openColumnMenu = (index: number, newCol: boolean) => {
         this._makeNewColumn = false;
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 05197d05f..37999484d 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -120,7 +120,14 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
                 <div className="row-cells">
                     {this.schemaView?.columnKeys?.map((key, index) => (
-                        <SchemaTableCell key={key} Document={this.rootDoc} fieldKey={key} columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth} isRowActive={this.props.isContentActive} />
+                        <SchemaTableCell
+                            key={key}
+                            Document={this.rootDoc}
+                            fieldKey={key}
+                            columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth}
+                            isRowActive={this.props.isContentActive}
+                            setColumnValues={(field, value) => this.schemaView?.setColumnValues(field, value) ?? false}
+                        />
                     ))}
                 </div>
             </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index d475c3b6f..13e45963e 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -15,6 +15,7 @@ export interface SchemaTableCellProps {
     fieldKey: string;
     columnWidth: number;
     isRowActive: () => boolean | undefined;
+    setColumnValues: (field: string, value: string) => boolean;
 }
 
 @observer
@@ -53,7 +54,12 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                     <EditableView
                         contents={<FieldView {...props} />}
                         GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
-                        SetValue={(value: string) => KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value)}
+                        SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => {
+                            if (shiftDown && enterKey) {
+                                this.props.setColumnValues(this.props.fieldKey, value);
+                            }
+                            return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value);
+                        }}
                         editing={this.props.isRowActive() ? undefined : false}
                     />
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 00150352f45e474705f7c4ebe7ce375e59b1f45a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 23 Mar 2023 21:04:33 -0400
Subject: fixed up pointer events so that schema and freeform items can be
 selected without selecting the collection.  however, dragging the item drags
 the collection unless the collection is selected.   cleaned up selected docs
 in schema to not keep a parallel observableSet to SelectionManager..

---
 .../collections/collectionSchema/CollectionSchemaView.tsx  | 14 +++++---------
 .../views/collections/collectionSchema/SchemaRowBox.tsx    | 11 +++++------
 src/client/views/nodes/DocumentView.tsx                    |  2 +-
 3 files changed, 11 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index a5f5ca2ae..5f795c5ac 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -54,7 +54,9 @@ export class CollectionSchemaView extends CollectionSubView() {
     public static _previewDividerWidth: number = 4;
 
     @observable _lastSelectedRow: number | undefined;
-    @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>();
+    @computed get _selectedDocs() {
+        return new Set(SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document)));
+    }
     @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
     @observable _colEles: HTMLDivElement[] = [];
     @observable _isDragging: boolean = false;
@@ -322,7 +324,6 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
-        this._selectedDocs.add(doc);
         const rowDocView = DocumentManager.Instance.getDocumentView(doc);
         if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection);
         this._lastSelectedRow = index;
@@ -330,7 +331,6 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     removeDocFromSelection = (doc: Doc) => {
-        if (this._selectedDocs.has(doc)) this._selectedDocs.delete(doc);
         const rowDocView = DocumentManager.Instance.getDocumentView(doc);
         if (rowDocView) SelectionManager.DeselectView(rowDocView);
         if (this._selectedDocs.size === 0) {
@@ -340,7 +340,6 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     clearSelection = () => {
-        this._selectedDocs.clear();
         SelectionManager.DeselectAll();
         this._lastSelectedRow = undefined;
     };
@@ -898,10 +897,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                     className="schema-table"
                     style={{
                         pointerEvents: this.isContentActive() ? 'all' : 'none',
-                    }}
-                    onPointerDown={action(() => {
-                        this.clearSelection();
-                    })}>
+                    }}>
                     <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
                         <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}>
                             <div
@@ -933,7 +929,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                     </div>
                     {this._columnMenuIndex !== undefined && this.renderColumnMenu}
                     {this._filterColumnIndex !== undefined && this.renderFilterMenu}
-                    <div className="schema-table-content" onPointerDown={e => e.stopPropagation()}>
+                    <div className="schema-table-content">
                         {this.childDocs.map((doc: Doc, index: number) => {
                             const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
                             let dref: Opt<DocumentView>;
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 37999484d..aa69e2b9c 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,19 +1,17 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, ObservableSet } from 'mobx';
+import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc } from '../../../../fields/Doc';
+import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
+import { DragManager } from '../../../util/DragManager';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
+import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
-import { Colors } from '../../global/globalEnums';
-import { DocCast, StrCast } from '../../../../fields/Types';
-import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
-import { DragManager } from '../../../util/DragManager';
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -40,6 +38,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
+        if (!this.isContentActive()) return;
         setupMoveUpEvents(this, e, e => this.schemaView?.startDrag(e, this.rootDoc, this.rowIndex) ?? true, emptyFunction, emptyFunction, false);
     };
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d7a61a797..c13934945 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1128,7 +1128,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             <div
                 className="documentView-contentsView"
                 style={{
-                    pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents,
+                    pointerEvents: (this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents) ?? 'all',
                     height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
                 }}>
                 {!this._retryThumb || !this.thumbShown() ? null : (
-- 
cgit v1.2.3-70-g09d2


From bdeeb202d264b2710f21b816f4e05a29793aa78e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 23 Mar 2023 21:17:43 -0400
Subject: fixed dragging schema rows.  fixed not being able to click row
 buttons unless schema view is active.

---
 .../collections/collectionSchema/CollectionSchemaView.tsx    | 12 +++++-------
 .../views/collections/collectionSchema/SchemaRowBox.tsx      |  3 ++-
 2 files changed, 7 insertions(+), 8 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 5f795c5ac..151e6cbef 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -884,6 +884,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     rowHeightFunc = () => CollectionSchemaView._rowHeight;
     rowClickScriptFunc = () => this.rowOnClickScript;
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
+    previewWidthFunc = () => this.previewWidth;
     render() {
         return (
             <div
@@ -893,11 +895,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                     this.createDashEventsTarget(ele);
                 }}
                 onDrop={this.onExternalDrop.bind(this)}>
-                <div
-                    className="schema-table"
-                    style={{
-                        pointerEvents: this.isContentActive() ? 'all' : 'none',
-                    }}>
+                <div className="schema-table">
                     <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
                         <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}>
                             <div
@@ -982,11 +980,11 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 focus={DocUtils.DefaultFocus}
                                 renderDepth={this.props.renderDepth}
                                 rootSelected={this.rootSelected}
-                                PanelWidth={() => this.previewWidth}
+                                PanelWidth={this.previewWidthFunc}
                                 PanelHeight={this.props.PanelHeight}
                                 isContentActive={returnTrue}
                                 isDocumentActive={returnFalse}
-                                ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0)}
+                                ScreenToLocalTransform={this.screenToLocal}
                                 docFilters={this.childDocFilters}
                                 docRangeFilters={this.childDocRangeFilters}
                                 searchFilterDocs={this.searchFilterDocs}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index aa69e2b9c..87284be70 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -39,7 +39,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     onRowPointerDown = (e: React.PointerEvent) => {
         if (!this.isContentActive()) return;
-        setupMoveUpEvents(this, e, e => this.schemaView?.startDrag(e, this.rootDoc, this.rowIndex) ?? true, emptyFunction, emptyFunction, false);
+        setupMoveUpEvents(this, e, e => this.schemaView?.startDrag(e, this.rootDoc, this.rowIndex) ?? true, emptyFunction, emptyFunction);
     };
 
     onPointerEnter = (e: any) => {
@@ -99,6 +99,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     className="row-menu"
                     style={{
                         width: CollectionSchemaView._rowMenuWidth,
+                        pointerEvents: !this.isContentActive() ? 'none' : undefined,
                     }}>
                     <div
                         className="schema-row-button"
-- 
cgit v1.2.3-70-g09d2


From 3a3e496dda5f9a1f5286a2c9f62524de59794ade Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 24 Mar 2023 03:37:41 -0400
Subject: overhaul of selection api so that schema and other views behave like
 freeform and use document views onClick for selection

---
 src/client/util/SelectionManager.ts                |  45 +--
 .../views/collections/CollectionCarousel3DView.tsx | 149 +++++-----
 src/client/views/collections/CollectionView.tsx    |   2 +-
 .../CollectionFreeFormLayoutEngines.tsx            |   7 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   9 +-
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |   2 -
 .../CollectionMulticolumnView.tsx                  |   3 +-
 .../CollectionMultirowView.tsx                     |   3 +-
 .../collectionSchema/CollectionSchemaView.tsx      | 318 +++++++++------------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  31 +-
 .../nodes/CollectionFreeFormDocumentView.scss      |   4 +-
 src/client/views/nodes/DocumentView.tsx            |  18 +-
 12 files changed, 282 insertions(+), 309 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 0f4f77588..313c255a0 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,3 +1,4 @@
+import { ModalManager } from '@material-ui/core';
 import { action, observable, ObservableMap } from 'mobx';
 import { computedFn } from 'mobx-utils';
 import { Doc, Opt } from '../../fields/Doc';
@@ -10,7 +11,8 @@ import { ScriptingGlobals } from './ScriptingGlobals';
 export namespace SelectionManager {
     class Manager {
         @observable IsDragging: boolean = false;
-        SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap();
+        SelectedViewsMap: ObservableMap<DocumentView, Doc> = new ObservableMap();
+        @observable SelectedViews: DocumentView[] = [];
         @observable SelectedSchemaDocument: Doc | undefined;
 
         @action
@@ -20,7 +22,7 @@ export namespace SelectionManager {
         @action
         SelectView(docView: DocumentView, ctrlPressed: boolean): void {
             // if doc is not in SelectedDocuments, add it
-            if (!manager.SelectedViews.get(docView) && docView.props.Document.type !== DocumentType.MARKER) {
+            if (!manager.SelectedViewsMap.get(docView) && docView.props.Document.type !== DocumentType.MARKER) {
                 if (!ctrlPressed) {
                     if (LinkManager.currentLink && !LinkManager.Links(docView.rootDoc).includes(LinkManager.currentLink) && docView.rootDoc !== LinkManager.currentLink) {
                         LinkManager.currentLink = undefined;
@@ -28,33 +30,38 @@ export namespace SelectionManager {
                     this.DeselectAll();
                 }
 
-                manager.SelectedViews.set(docView, docView.rootDoc);
+                manager.SelectedViews.push(docView);
+                manager.SelectedViewsMap.set(docView, docView.rootDoc);
                 docView.props.whenChildContentsActiveChanged(true);
-            } else if (!ctrlPressed && (Array.from(manager.SelectedViews.entries()).length > 1 || manager.SelectedSchemaDocument)) {
-                Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false));
+            } else if (!ctrlPressed && (Array.from(manager.SelectedViewsMap.entries()).length > 1 || manager.SelectedSchemaDocument)) {
+                Array.from(manager.SelectedViewsMap.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false));
                 manager.SelectedSchemaDocument = undefined;
-                manager.SelectedViews.clear();
-                manager.SelectedViews.set(docView, docView.rootDoc);
+                manager.SelectedViews.length = 0;
+                manager.SelectedViewsMap.clear();
+                manager.SelectedViews.push(docView);
+                manager.SelectedViewsMap.set(docView, docView.rootDoc);
             }
         }
         @action
-        DeselectView(docView: DocumentView): void {
-            if (manager.SelectedViews.get(docView)) {
-                manager.SelectedViews.delete(docView);
+        DeselectView(docView?: DocumentView): void {
+            if (docView && manager.SelectedViewsMap.get(docView)) {
+                manager.SelectedViewsMap.delete(docView);
+                manager.SelectedViews.splice(manager.SelectedViews.indexOf(docView), 1);
                 docView.props.whenChildContentsActiveChanged(false);
             }
         }
         @action
         DeselectAll(): void {
             manager.SelectedSchemaDocument = undefined;
-            Array.from(manager.SelectedViews.keys()).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
-            manager.SelectedViews.clear();
+            Array.from(manager.SelectedViewsMap.keys()).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
+            manager.SelectedViewsMap.clear();
+            manager.SelectedViews.length = 0;
         }
     }
 
     const manager = new Manager();
 
-    export function DeselectView(docView: DocumentView): void {
+    export function DeselectView(docView?: DocumentView): void {
         manager.DeselectView(docView);
     }
     export function SelectView(docView: DocumentView, ctrlPressed: boolean): void {
@@ -67,7 +74,7 @@ export namespace SelectionManager {
 
     const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {
         // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
-        return manager.SelectedViews.get(doc) ? true : false;
+        return manager.SelectedViewsMap.get(doc) ? true : false;
     });
     // computed functions, such as used in IsSelected generate errors if they're called outside of a
     // reaction context.  Specifying the context with 'outsideReaction' allows an efficiency feature
@@ -76,7 +83,7 @@ export namespace SelectionManager {
         return !doc
             ? false
             : outsideReaction
-            ? manager.SelectedViews.get(doc)
+            ? manager.SelectedViewsMap.get(doc)
                 ? true
                 : false // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
             : IsSelectedCache(doc);
@@ -85,7 +92,7 @@ export namespace SelectionManager {
     export function DeselectAll(except?: Doc): void {
         let found: DocumentView | undefined = undefined;
         if (except) {
-            for (const view of Array.from(manager.SelectedViews.keys())) {
+            for (const view of Array.from(manager.SelectedViewsMap.keys())) {
                 if (view.props.Document === except) found = view;
             }
         }
@@ -95,13 +102,15 @@ export namespace SelectionManager {
     }
 
     export function Views(): Array<DocumentView> {
-        return Array.from(manager.SelectedViews.keys()); //.filter(dv => manager.SelectedViews.get(dv)?._viewType !== CollectionViewType.Docking);
+        return manager.SelectedViews;
+        // Array.from(manager.SelectedViewsMap.keys()); //.filter(dv => manager.SelectedViews.get(dv)?._viewType !== CollectionViewType.Docking);
     }
     export function SelectedSchemaDoc(): Doc | undefined {
         return manager.SelectedSchemaDocument;
     }
     export function Docs(): Doc[] {
-        return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
+        return manager.SelectedViews.map(dv => dv.rootDoc).filter(doc => doc?._viewType !== CollectionViewType.Docking);
+        // Array.from(manager.SelectedViewsMap.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
     }
 }
 ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) {
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 01f41869e..57ff1b292 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -9,7 +9,7 @@ import { OmitKeys, returnFalse, Utils } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { DocumentView } from '../nodes/DocumentView';
 import { StyleProp } from '../StyleProvider';
-import "./CollectionCarousel3DView.scss";
+import './CollectionCarousel3DView.scss';
 import { CollectionSubView } from './CollectionSubView';
 
 @observer
@@ -20,134 +20,143 @@ export class CollectionCarousel3DView extends CollectionSubView() {
 
     private _dropDisposer?: DragManager.DragDropDisposer;
 
-    componentWillUnmount() { this._dropDisposer?.(); }
+    componentWillUnmount() {
+        this._dropDisposer?.();
+    }
 
-    protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+        //used for stacking and masonry view
         this._dropDisposer?.();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
         }
-    }
+    };
 
     panelWidth = () => this.props.PanelWidth() / 3;
     panelHeight = () => this.props.PanelHeight() * 0.6;
     onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+    isChildContentActive = () => (this.isContentActive() ? true : false);
+
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._itemIndex);
-        const displayDoc = (childPair: { layout: Doc, data: Doc }) => {
-            return <DocumentView  {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "childLayoutTemplate", "childLayoutString"]).omit}
-                onDoubleClick={this.onChildDoubleClick}
-                renderDepth={this.props.renderDepth + 1}
-                LayoutTemplate={this.props.childLayoutTemplate}
-                LayoutTemplateString={this.props.childLayoutString}
-                Document={childPair.layout}
-                DataDoc={childPair.data}
-                PanelWidth={this.panelWidth}
-                PanelHeight={this.panelHeight}
-                bringToFront={returnFalse}
-            />;
+        const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
+            return (
+                <DocumentView
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'childLayoutTemplate', 'childLayoutString']).omit}
+                    onDoubleClick={this.onChildDoubleClick}
+                    renderDepth={this.props.renderDepth + 1}
+                    LayoutTemplate={this.props.childLayoutTemplate}
+                    LayoutTemplateString={this.props.childLayoutString}
+                    Document={childPair.layout}
+                    DataDoc={childPair.data}
+                    isContentActive={this.isChildContentActive}
+                    isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+                    PanelWidth={this.panelWidth}
+                    PanelHeight={this.panelHeight}
+                    bringToFront={returnFalse}
+                />
+            );
         };
 
-        return (this.childLayoutPairs.map((childPair, index) => {
+        return this.childLayoutPairs.map((childPair, index) => {
             return (
-                <div key={childPair.layout[Id]}
-                    className={`collectionCarousel3DView-item${index === currentIndex ? "-active" : ""} ${index}`}
-                    style={index === currentIndex ?
-                        { opacity: '1', transform: 'scale(1.3)', width: this.panelWidth() } :
-                        { opacity: '0.5', transform: 'scale(0.6)', userSelect: 'none', width: this.panelWidth() }}>
+                <div
+                    key={childPair.layout[Id]}
+                    className={`collectionCarousel3DView-item${index === currentIndex ? '-active' : ''} ${index}`}
+                    style={index === currentIndex ? { opacity: '1', transform: 'scale(1.3)', width: this.panelWidth() } : { opacity: '0.5', transform: 'scale(0.6)', userSelect: 'none', width: this.panelWidth() }}>
                     {displayDoc(childPair)}
-                </div>);
-        }));
+                </div>
+            );
+        });
     }
 
     changeSlide = (direction: number) => {
         this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) + direction + this.childLayoutPairs.length) % this.childLayoutPairs.length;
-    }
+    };
 
     onArrowClick = (e: React.MouseEvent, direction: number) => {
         e.stopPropagation();
         this.changeSlide(direction);
-        !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = (direction === 1) ? "fwd" : "back"); // while autoscroll is on, keep the other autoscroll button hidden
+        !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = direction === 1 ? 'fwd' : 'back'); // while autoscroll is on, keep the other autoscroll button hidden
         !this.layoutDoc.autoScrollOn && this.fadeScrollButton(); // keep pause button visible while autoscroll is on
-    }
+    };
 
     interval?: number;
     startAutoScroll = (direction: number) => {
         this.interval = window.setInterval(() => {
             this.changeSlide(direction);
         }, this.scrollSpeed);
-    }
+    };
 
     stopAutoScroll = () => {
         window.clearInterval(this.interval);
         this.interval = undefined;
         this.fadeScrollButton();
-    }
+    };
 
     toggleAutoScroll = (direction: number) => {
         this.layoutDoc.autoScrollOn = this.layoutDoc.autoScrollOn ? false : true;
         this.layoutDoc.autoScrollOn ? this.startAutoScroll(direction) : this.stopAutoScroll();
-    }
+    };
 
     fadeScrollButton = () => {
         window.setTimeout(() => {
-            !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = "none"); //fade away after 1.5s if it's not clicked.
+            !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = 'none'); //fade away after 1.5s if it's not clicked.
         }, 1500);
-    }
+    };
 
     @computed get buttons() {
         if (!this.props.isContentActive()) return null;
-        return <div className="arrow-buttons" >
-            <div key="back" className="carousel3DView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}
-                onClick={(e) => this.onArrowClick(e, -1)}
-            >
-                <FontAwesomeIcon icon={"angle-left"} size={"2x"} />
-            </div>
-            <div key="fwd" className="carousel3DView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}
-                onClick={(e) => this.onArrowClick(e, 1)}
-            >
-                <FontAwesomeIcon icon={"angle-right"} size={"2x"} />
+        return (
+            <div className="arrow-buttons">
+                <div key="back" className="carousel3DView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={e => this.onArrowClick(e, -1)}>
+                    <FontAwesomeIcon icon={'angle-left'} size={'2x'} />
+                </div>
+                <div key="fwd" className="carousel3DView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={e => this.onArrowClick(e, 1)}>
+                    <FontAwesomeIcon icon={'angle-right'} size={'2x'} />
+                </div>
+                {this.autoScrollButton}
             </div>
-            {this.autoScrollButton}
-        </div>;
+        );
     }
 
     @computed get autoScrollButton() {
         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)}>
-                {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)}>
-                {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={"pause"} size={"1x"} /> : <FontAwesomeIcon icon={"angle-double-right"} size={"1x"} />}
-            </div>
-        </>;
+        return (
+            <>
+                <div className={`carousel3DView-back-scroll${whichButton === 'back' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.props.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)}>
+                    {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={'pause'} size={'1x'} /> : <FontAwesomeIcon icon={'angle-double-right'} size={'1x'} />}
+                </div>
+            </>
+        );
     }
 
     @computed get dots() {
-        return (this.childLayoutPairs.map((_child, index) =>
-            <div key={Utils.GenerateGuid()} className={`dot${index === NumCast(this.layoutDoc._itemIndex) ? "-active" : ""}`}
-                onClick={() => this.layoutDoc._itemIndex = index} />));
+        return this.childLayoutPairs.map((_child, index) => <div key={Utils.GenerateGuid()} className={`dot${index === NumCast(this.layoutDoc._itemIndex) ? '-active' : ''}`} onClick={() => (this.layoutDoc._itemIndex = index)} />);
     }
 
     render() {
         const index = NumCast(this.layoutDoc._itemIndex);
         const translateX = this.panelWidth() * (1 - index);
 
-        return <div className="collectionCarousel3DView-outer" ref={this.createDashEventsTarget}
-            style={{
-                background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
-                color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
-            }}  >
-            <div className="carousel-wrapper" style={{ transform: `translateX(${translateX}px)` }}>
-                {this.content}
-            </div>
-            {this.props.Document._chromeHidden ? (null) : this.buttons}
-            <div className="dot-bar">
-                {this.dots}
+        return (
+            <div
+                className="collectionCarousel3DView-outer"
+                ref={this.createDashEventsTarget}
+                style={{
+                    background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+                    color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
+                }}>
+                <div className="carousel-wrapper" style={{ transform: `translateX(${translateX}px)` }}>
+                    {this.content}
+                </div>
+                {this.props.Document._chromeHidden ? null : this.buttons}
+                <div className="dot-bar">{this.dots}</div>
             </div>
-        </div>;
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index eafa50d27..51624689e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -257,7 +257,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         return StrCast(this.rootDoc.childLayoutString, this.props.childLayoutString);
     }
 
-    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive();
+    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() || this.isAnyChildContentActive();
 
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index fa0695fb2..81b0c4d8a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -116,6 +116,8 @@ export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc
             zIndex: NumCast(layout.zIndex),
             pair: { layout, data },
             replica: '',
+            color: 'white',
+            backgroundColor: 'white',
         });
     });
     const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
@@ -408,7 +410,7 @@ function normalizeResults(
         .map(ele => {
             const newPosRaw = ele[1];
             if (newPosRaw) {
-                const newPos = {
+                const newPos: PoolData = {
                     x: newPosRaw.x * scale,
                     y: newPosRaw.y * scale,
                     z: newPosRaw.z,
@@ -417,6 +419,9 @@ function normalizeResults(
                     zIndex: newPosRaw.zIndex,
                     width: (newPosRaw.width || 0) * scale,
                     height: newPosRaw.height! * scale,
+                    backgroundColor: newPosRaw.backgroundColor,
+                    opacity: newPosRaw.opacity,
+                    color: newPosRaw.color,
                     pair: ele[1].pair,
                 };
                 poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3a8edb1a5..4f81af95d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1291,10 +1291,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     pointerEvents = () => {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
-        const pointerEvents =
-            this.props.isContentActive() === false || DocumentDecorations.Instance.Interacting
-                ? 'none'
-                : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
+        const pointerEvents = DocumentDecorations.Instance.Interacting
+            ? 'none'
+            : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
         return pointerEvents;
     };
     getChildDocView(entry: PoolData) {
@@ -1995,7 +1994,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         TraceMobx();
         return (
             <div
-                className={'collectionfreeformview-container'}
+                className="collectionfreeformview-container"
                 ref={this.createDashEventsTarget}
                 onWheel={this.onPointerWheel}
                 onClick={this.onClick}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 9581563ce..c9168d40a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -3,8 +3,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { observer } from 'mobx-react';
 import { unimplementedFunction } from '../../../../Utils';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { SelectionManager } from '../../../util/SelectionManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 
 @observer
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 88d045fa7..b73b1d779 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -2,14 +2,13 @@ import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
-import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
-import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionMulticolumnView.scss';
 import ResizeBar from './MulticolumnResizer';
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index f18917bef..0cca83803 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -2,14 +2,13 @@ import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
-import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
-import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 151e6cbef..ad31113a2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,15 +1,17 @@
 import React = require('react');
-import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTransparent, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
+import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { Transform } from '../../../util/Transform';
@@ -17,18 +19,14 @@ import { undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { EditableView } from '../../EditableView';
-import { DocComponentView, DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { KeyValueBox } from '../../nodes/KeyValueBox';
+import { DefaultStyleProvider } from '../../StyleProvider';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { ScriptField } from '../../../../fields/ScriptField';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconButton } from 'browndash-components';
-import { KeyValueBox } from '../../nodes/KeyValueBox';
 
 export enum ColumnType {
     Number,
@@ -43,7 +41,6 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate',
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
     private _ref: HTMLDivElement | null = null;
-    private _selectedDocSortedArray: Doc[] = [];
     private _closestDropIndex: number = 0;
     private _previewRef: HTMLDivElement | null = null;
     private _makeNewColumn: boolean = false;
@@ -53,13 +50,11 @@ export class CollectionSchemaView extends CollectionSubView() {
     public static _rowMenuWidth: number = 100;
     public static _previewDividerWidth: number = 4;
 
-    @observable _lastSelectedRow: number | undefined;
     @computed get _selectedDocs() {
-        return new Set(SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document)));
+        return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document));
     }
     @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
     @observable _colEles: HTMLDivElement[] = [];
-    @observable _isDragging: boolean = false;
     @observable _displayColumnWidths: number[] | undefined;
     @observable _columnMenuIndex: number | undefined;
     @observable _menuOptions: string[] = [];
@@ -133,7 +128,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     componentDidMount() {
-        this.props.setContentView?.(this as DocComponentView);
+        this.props.setContentView?.(this);
         document.addEventListener('keydown', this.onKeyDown);
     }
 
@@ -143,24 +138,36 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     onKeyDown = (e: KeyboardEvent) => {
-        if (this._selectedDocs.size > 0) {
-            if (e.key == 'ArrowDown') {
-                const lastDoc = Array.from(this._selectedDocs.values()).lastElement();
-                const lastIndex = this.rowIndex(lastDoc);
-                if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
-                    !e.shiftKey && this.clearSelection();
-                    const newDoc = this.childDocs[lastIndex + 1];
-                    this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
-                }
-            }
-            if (e.key == 'ArrowUp') {
-                const firstDoc = Array.from(this._selectedDocs.values())[0];
-                const firstIndex = this.rowIndex(firstDoc);
-                if (firstIndex > 0 && firstIndex < this.childDocs.length) {
-                    !e.shiftKey && this.clearSelection();
-                    const newDoc = this.childDocs[firstIndex - 1];
-                    this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
-                }
+        if (this._selectedDocs.length > 0) {
+            switch (e.key) {
+                case 'ArrowDown':
+                    {
+                        const lastDoc = this._selectedDocs.lastElement();
+                        const lastIndex = this.rowIndex(lastDoc);
+                        const curDoc = this.childDocs[lastIndex];
+                        if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
+                            !e.shiftKey && this.clearSelection();
+                            const newDoc = this.childDocs[lastIndex + 1];
+                            if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+                            else this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
+                        }
+                        e.stopPropagation();
+                    }
+                    break;
+                case 'ArrowUp':
+                    {
+                        const firstDoc = this._selectedDocs.lastElement();
+                        const firstIndex = this.rowIndex(firstDoc);
+                        const curDoc = this.childDocs[firstIndex];
+                        if (firstIndex > 0 && firstIndex < this.childDocs.length) {
+                            !e.shiftKey && this.clearSelection();
+                            const newDoc = this.childDocs[firstIndex - 1];
+                            if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+                            else this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
+                        }
+                        e.stopPropagation();
+                    }
+                    break;
             }
         }
     };
@@ -326,60 +333,25 @@ export class CollectionSchemaView extends CollectionSubView() {
     addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
         const rowDocView = DocumentManager.Instance.getDocumentView(doc);
         if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection);
-        this._lastSelectedRow = index;
-    };
-
-    @action
-    removeDocFromSelection = (doc: Doc) => {
-        const rowDocView = DocumentManager.Instance.getDocumentView(doc);
-        if (rowDocView) SelectionManager.DeselectView(rowDocView);
-        if (this._selectedDocs.size === 0) {
-            this._lastSelectedRow = undefined;
-        }
     };
 
     @action
-    clearSelection = () => {
-        SelectionManager.DeselectAll();
-        this._lastSelectedRow = undefined;
-    };
-
-    rowOnClickScript = ScriptField.MakeFunction('scriptContext.selectRow(self, shiftKey, ctrlKey || metaKey)', { scriptContext: 'any', shiftKey: 'boolean', ctrlKey: 'boolean', metaKey: 'boolean' })!;
-
-    @action
-    selectRow = (doc: Doc, shift: boolean, ctrl: boolean) => {
-        const index = this.childDocs.indexOf(doc);
-        if (index < 0) return;
-        if (shift && this._lastSelectedRow !== undefined) {
-            const startRow = Math.min(this._lastSelectedRow, index);
-            const endRow = Math.max(this._lastSelectedRow, index);
-            for (let i = startRow; i <= endRow; i++) {
-                const currDoc = this.childDocs[i];
-                if (!this._selectedDocs.has(currDoc)) this.addDocToSelection(currDoc, true, i);
-            }
-            this._lastSelectedRow = index;
-        } else if (ctrl) {
-            if (!this._selectedDocs.has(doc)) {
-                this.addDocToSelection(doc, true, index);
-            } else {
-                this.removeDocFromSelection(doc);
-            }
-        } else {
-            if (!this._selectedDocs.has(doc)) {
-                this.clearSelection();
-                this.addDocToSelection(doc, false, index);
-            }
+    clearSelection = () => SelectionManager.DeselectAll();
+
+    selectRows = (rootDoc: Doc, lastSelected: Doc) => {
+        const index = this.childDocs.indexOf(rootDoc);
+        const lastSelectedRow = this.childDocs.indexOf(lastSelected);
+        const startRow = Math.min(lastSelectedRow, index);
+        const endRow = Math.max(lastSelectedRow, index);
+        for (let i = startRow; i <= endRow; i++) {
+            const currDoc = this.childDocs[i];
+            if (!this._selectedDocs.includes(currDoc)) this.addDocToSelection(currDoc, true, i);
         }
     };
 
-    @action
-    sortedSelectedDocs = (): Doc[] => {
-        return this.childDocs.filter(doc => this._selectedDocs.has(doc));
-    };
+    sortedSelectedDocs = () => this.childDocs.filter(doc => this._selectedDocs.includes(doc));
 
-    setDropIndex = (index: number) => {
-        this._closestDropIndex = index;
-    };
+    setDropIndex = (index: number) => (this._closestDropIndex = index);
 
     @action
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
@@ -394,17 +366,20 @@ export class CollectionSchemaView extends CollectionSubView() {
                 return total + curr;
             }, CollectionSchemaView._rowMenuWidth);
             this.swapColumns(de.complete.columnDragData.colIndex, i);
+            e.stopPropagation();
             return true;
         }
-        if (super.onInternalDrop(e, de)) {
-            this._isDragging = false;
-            const pushedDocs: Doc[] = this.childDocs.filter((doc: Doc, index: number) => index >= this._closestDropIndex && !this._selectedDocs.has(doc));
+        const draggedDocs = de.complete.docDragData?.draggedDocuments;
+        if (draggedDocs && super.onInternalDrop(e, de)) {
+            const pushedDocs = this.childDocs.filter((doc, index) => index >= this._closestDropIndex && !draggedDocs.includes(doc));
             this.props.removeDocument?.(pushedDocs);
-            this.props.removeDocument?.(this._selectedDocSortedArray);
-            this.addDocument(this._selectedDocSortedArray);
+            this.props.removeDocument?.(draggedDocs);
+            this.addDocument(draggedDocs);
             this.addDocument(pushedDocs);
             this.setSort(undefined);
-            this.clearSelection();
+            SelectionManager.DeselectAll();
+            setTimeout(() => draggedDocs.forEach(doc => DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true))), 100);
+            e.stopPropagation();
             return true;
         }
         return false;
@@ -412,46 +387,11 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     onExternalDrop = async (e: React.DragEvent): Promise<void> => {
-        super.onExternalDrop(
-            e,
-            {},
-            undoBatch(
-                action(docus => {
-                    this._isDragging = false;
-                    docus.map((doc: Doc) => {
-                        this.addDocument(doc);
-                    });
-                })
-            )
-        );
+        super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc)))));
         this.setSort(undefined);
     };
 
-    @action
-    startDrag = (e: PointerEvent, doc: Doc, index: number) => {
-        if (!this._selectedDocs.has(doc)) {
-            this.clearSelection();
-            this.addDocToSelection(doc, false, index);
-        }
-        this._isDragging = true;
-        this._selectedDocSortedArray = this.sortedSelectedDocs();
-        const dragData = new DragManager.DocumentDragData(this._selectedDocSortedArray, 'move');
-        dragData.moveDocument = this.props.moveDocument;
-        const dragItem: HTMLElement[] = Array.from(this._selectedDocs.values()).map((doc: Doc) => this._rowEles.get(doc));
-
-        DragManager.StartDocumentDrag(
-            dragItem.map(ele => ele),
-            dragData,
-            e.clientX,
-            e.clientY,
-            undefined
-        );
-        return true;
-    };
-
-    onDividerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
-    };
+    onDividerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
 
     @action
     onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
@@ -557,9 +497,6 @@ export class CollectionSchemaView extends CollectionSubView() {
         return undefined;
     };
 
-    isChildContentActive = () =>
-        this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
-
     @computed get fieldDefaultInput() {
         switch (this._newFieldType) {
             case ColumnType.Number:
@@ -644,23 +581,17 @@ export class CollectionSchemaView extends CollectionSubView() {
         ContextMenu.Instance.clearItems();
         ContextMenu.Instance.addItem({
             description: 'Change field',
-            event: () => {
-                this.openColumnMenu(index, false);
-            },
+            event: () => this.openColumnMenu(index, false),
             icon: 'pencil-alt',
         });
         ContextMenu.Instance.addItem({
             description: 'Filter field',
-            event: () => {
-                this.openFilterMenu(index);
-            },
+            event: () => this.openFilterMenu(index),
             icon: 'filter',
         });
         ContextMenu.Instance.addItem({
             description: 'Delete column',
-            event: () => {
-                this.removeColumn(index);
-            },
+            event: () => this.removeColumn(index),
             icon: 'trash',
         });
         ContextMenu.Instance.displayMenu(x, y, undefined, false);
@@ -672,9 +603,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._menuOptions = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
     };
 
-    getFieldFilters = (field: string) => {
-        return StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
-    };
+    getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
 
     removeFieldFilters = (field: string) => {
         this.getFieldFilters(field).forEach(filter => {
@@ -694,9 +623,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     };
 
     @action
-    updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this._filterValue = e.target.value;
-    };
+    updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterValue = e.target.value);
 
     @computed get newFieldMenu() {
         return (
@@ -705,7 +632,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     <input
                         type="radio"
                         name="newFieldType"
-                        id=""
                         checked={this._newFieldType == ColumnType.Number}
                         onChange={action(() => {
                             this._newFieldType = ColumnType.Number;
@@ -718,7 +644,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     <input
                         type="radio"
                         name="newFieldType"
-                        id=""
                         checked={this._newFieldType == ColumnType.Boolean}
                         onChange={action(() => {
                             this._newFieldType = ColumnType.Boolean;
@@ -731,7 +656,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     <input
                         type="radio"
                         name="newFieldType"
-                        id=""
                         checked={this._newFieldType == ColumnType.String}
                         onChange={action(() => {
                             this._newFieldType = ColumnType.String;
@@ -880,13 +804,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         );
     }
 
-    tableWidthFunc = () => this.tableWidth;
-    rowHeightFunc = () => CollectionSchemaView._rowHeight;
-    rowClickScriptFunc = () => this.rowOnClickScript;
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
     previewWidthFunc = () => this.previewWidth;
     render() {
+        trace();
         return (
             <div
                 className="collectionSchemaView"
@@ -894,6 +816,12 @@ export class CollectionSchemaView extends CollectionSubView() {
                     this._ref = ele;
                     this.createDashEventsTarget(ele);
                 }}
+                onPointerDown={e => {
+                    // this is analogous to the panning code for a freeform view.
+                    // however, schema views don't pan so it does nothing.  but it does eat the pointerDown event
+                    // if the content is active to prevent the schema from being dragged
+                    this.isContentActive() && setupMoveUpEvents(this, e, returnFalse, emptyFunction, emptyFunction, false);
+                }}
                 onDrop={this.onExternalDrop.bind(this)}>
                 <div className="schema-table">
                     <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
@@ -927,53 +855,16 @@ export class CollectionSchemaView extends CollectionSubView() {
                     </div>
                     {this._columnMenuIndex !== undefined && this.renderColumnMenu}
                     {this._filterColumnIndex !== undefined && this.renderFilterMenu}
-                    <div className="schema-table-content">
-                        {this.childDocs.map((doc: Doc, index: number) => {
-                            const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
-                            let dref: Opt<DocumentView>;
-                            return (
-                                <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
-                                    <DocumentView
-                                        {...this.props}
-                                        ref={r => (dref = r || undefined)}
-                                        LayoutTemplate={this.props.childLayoutTemplate}
-                                        LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
-                                        Document={doc}
-                                        DataDoc={dataDoc}
-                                        ContainingCollectionView={this.props.CollectionView}
-                                        ContainingCollectionDoc={this.Document}
-                                        PanelWidth={this.tableWidthFunc}
-                                        PanelHeight={this.rowHeightFunc}
-                                        styleProvider={DefaultStyleProvider}
-                                        focus={this.focusDocument}
-                                        docFilters={this.childDocFilters}
-                                        docRangeFilters={this.childDocRangeFilters}
-                                        searchFilterDocs={this.searchFilterDocs}
-                                        rootSelected={this.rootSelected}
-                                        ScreenToLocalTransform={Transform.Identity}
-                                        bringToFront={emptyFunction}
-                                        isDocumentActive={this.isContentActive}
-                                        isContentActive={emptyFunction}
-                                        hideDecorations={true}
-                                        hideTitle={true}
-                                        hideDocumentButtonBar={true}
-                                        hideLinkAnchors={true}
-                                        fitWidth={returnTrue}
-                                        onClick={this.rowClickScriptFunc}
-                                        scriptContext={this}
-                                    />
-                                </div>
-                            );
-                        })}
-                    </div>
+                    <CollectionSchemaViewDocs schema={this} />
+
                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
                 </div>
                 {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
                 {this.previewWidth > 0 && (
                     <div style={{ width: `${this.previewWidth}px` }} ref={ref => (this._previewRef = ref)}>
-                        {this._lastSelectedRow !== undefined && (
+                        {Array.from(this._selectedDocs).lastElement() && (
                             <DocumentView
-                                Document={this.childDocs[this._lastSelectedRow]}
+                                Document={Array.from(this._selectedDocs).lastElement()}
                                 DataDoc={undefined}
                                 fitContentsToBox={returnTrue}
                                 dontCenter={'y'}
@@ -1007,3 +898,54 @@ export class CollectionSchemaView extends CollectionSubView() {
         );
     }
 }
+
+interface CollectionSchemaViewDocsProps {
+    schema: CollectionSchemaView;
+}
+
+@observer
+class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> {
+    tableWidthFunc = () => this.props.schema.tableWidth;
+    rowHeightFunc = () => CollectionSchemaView._rowHeight;
+    render() {
+        return (
+            <div className="schema-table-content">
+                {this.props.schema.childDocs.map((doc: Doc, index: number) => {
+                    const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.schema.props.DataDoc;
+                    return (
+                        <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
+                            <DocumentView
+                                {...this.props.schema.props}
+                                LayoutTemplate={this.props.schema.props.childLayoutTemplate}
+                                LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey)}
+                                Document={doc}
+                                DataDoc={dataDoc}
+                                ContainingCollectionView={this.props.schema.props.CollectionView}
+                                ContainingCollectionDoc={this.props.schema.Document}
+                                PanelWidth={this.tableWidthFunc}
+                                PanelHeight={this.rowHeightFunc}
+                                styleProvider={DefaultStyleProvider}
+                                focus={this.props.schema.focusDocument}
+                                docFilters={this.props.schema.childDocFilters}
+                                docRangeFilters={this.props.schema.childDocRangeFilters}
+                                searchFilterDocs={this.props.schema.searchFilterDocs}
+                                rootSelected={this.props.schema.rootSelected}
+                                ScreenToLocalTransform={Transform.Identity}
+                                bringToFront={emptyFunction}
+                                isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive}
+                                isContentActive={emptyFunction}
+                                whenChildContentsActiveChanged={active => this.props.schema.props.whenChildContentsActiveChanged(active)}
+                                hideDecorations={true}
+                                hideTitle={true}
+                                hideDocumentButtonBar={true}
+                                hideLinkAnchors={true}
+                                fitWidth={returnTrue}
+                                scriptContext={this}
+                            />
+                        </div>
+                    );
+                })}
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 87284be70..0c8c0ee59 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,9 +1,9 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed } from 'mobx';
+import { computed } from 'mobx';
 import { observer } from 'mobx-react';
-import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
@@ -36,24 +36,30 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.schemaView?.rowIndex(this.rootDoc) ?? -1;
     }
 
-    @action
-    onRowPointerDown = (e: React.PointerEvent) => {
-        if (!this.isContentActive()) return;
-        setupMoveUpEvents(this, e, e => this.schemaView?.startDrag(e, this.rootDoc, this.rowIndex) ?? true, emptyFunction, emptyFunction);
+    componentDidMount(): void {
+        this.props.setContentView?.(this);
+    }
+
+    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);
+        else {
+            this.props.select?.(ctrlKey);
+        }
     };
 
     onPointerEnter = (e: any) => {
-        if (!this.schemaView?._isDragging) return;
+        //if (!this.schemaView?._isDragging) return;
+        if (!SnappingManager.GetIsDragging()) return;
         document.removeEventListener('pointermove', this.onPointerMove);
         document.addEventListener('pointermove', this.onPointerMove);
     };
 
     onPointerMove = (e: any) => {
-        if (!this.schemaView?._isDragging) return;
-        let dragIsRow: boolean = true;
-        DragManager.docsBeingDragged.forEach(doc => {
-            dragIsRow = this.schemaView?._selectedDocs.has(doc) ?? false;
-        });
+        if (!SnappingManager.GetIsDragging()) return;
+        const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.context === this.schemaDoc); //  this.schemaView?._selectedDocs.has(doc) ?? false;
+
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
             const y = e.clientY - rect.top; //y position within the element.
@@ -88,7 +94,6 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         ? { height: CollectionSchemaView._rowHeight, backgroundColor: Colors.LIGHT_BLUE, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined /*, opacity: this.props.dragging ? 0.5 : 1 */ }
                         : { height: CollectionSchemaView._rowHeight, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }
                 }
-                onPointerDown={this.onRowPointerDown}
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
index 724394025..f99011b8f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.scss
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
@@ -5,5 +5,5 @@
     touch-action: manipulation;
     top: 0;
     left: 0;
-    pointer-events: none;
-}
\ No newline at end of file
+    //pointer-events: none;
+}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c13934945..1f717932e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -121,6 +121,7 @@ export interface DocComponentView {
     addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
     reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void; // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
+    select?: (ctrlKey: boolean, shiftKey: boolean) => void;
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
     getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
@@ -236,7 +237,7 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
     NativeHeight: () => number;
     isSelected: (outsideReaction?: boolean) => boolean;
     isHovering: () => boolean;
-    select: (ctrlPressed: boolean) => void;
+    select: (ctrlPressed: boolean, shiftPress?: boolean) => void;
     DocumentView: () => DocumentView;
     viewPath: () => DocumentView[];
 }
@@ -538,7 +539,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
         if (this._mainCont.current) {
-            const dragData = new DragManager.DocumentDragData([this.props.Document]);
+            const views = SelectionManager.Views().filter(dv => dv.docView?._mainCont.current);
+            const selected = views.some(dv => dv.rootDoc === this.Document) ? views : [this.props.DocumentView()];
+            const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.rootDoc));
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
             dragData.offset = this.props
                 .ScreenToLocalTransform()
@@ -551,8 +554,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.canEmbed = this.props.canEmbedOnDrag;
             const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
             ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
-            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) }, () =>
-                setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))
+            DragManager.StartDocumentDrag(
+                selected.map(dv => dv.docView!._mainCont.current!),
+                dragData,
+                x,
+                y,
+                { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) },
+                () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))
             ); // this needs to happen after the drop event is processed.
             ffview?.setupDragLines(false);
         }
@@ -659,7 +667,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         }),
                         350
                     );
-                    this.props.select(e.ctrlKey || e.shiftKey);
+                    (this._componentView?.select ?? this.props.select)(e.ctrlKey || e.metaKey, e.shiftKey);
                 }
                 preventDefault = false;
             }
-- 
cgit v1.2.3-70-g09d2


From fd71eb1a130058207b45cc6a1da0dbd97f6dd4f1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 24 Mar 2023 23:18:44 -0400
Subject: fixed showing keyValueBox when document opacity is 0 or it is hidden.
  fixed toggling link targets.  fixed sorting and undoing schema view changes.

---
 src/client/util/DocumentManager.ts                 |  1 +
 src/client/views/StyleProvider.tsx                 |  5 +-
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 +
 .../collectionSchema/CollectionSchemaView.tsx      | 81 +++++++++++-----------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  1 -
 src/client/views/nodes/DocumentView.tsx            |  9 ++-
 src/client/views/nodes/LabelBox.tsx                |  1 +
 8 files changed, 54 insertions(+), 47 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 947613801..ccf370662 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -262,6 +262,7 @@ export class DocumentManager {
         finished?: () => void
     ) => {
         const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
+        if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
         let rootContextView = await new Promise<DocumentView>(res => {
             const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc));
             if (viewIndex !== -1) return res(this.getDocumentView(docContextPath[viewIndex])!);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index d1e85a65b..1b5eb3342 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -146,6 +146,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return (
                 (doc &&
                     !doc.presentationTargetDoc &&
+                    !props?.LayoutTemplateString?.includes(KeyValueBox.name) &&
                     props?.showTitle?.() !== '' &&
                     StrCast(
                         doc._showTitle,
@@ -169,7 +170,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             if (!backColor) return undefined;
             return lightOrDark(backColor);
         case StyleProp.Hidden:
-            return BoolCast(doc?.hidden);
+            return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? false : BoolCast(doc?.hidden);
         case StyleProp.BorderRounding:
             return StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.borderRounding, doc?._viewType === CollectionViewType.Pile ? '50%' : ''));
         case StyleProp.TitleHeight:
@@ -288,7 +289,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         }
         case StyleProp.PointerEvents:
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
-            if (MainView.Instance._exploreMode) return isInk ? 'visiblePainted' : 'all';
+            if (MainView.Instance._exploreMode || doc?.unrendered) return isInk ? 'visiblePainted' : 'all';
             if (doc?.pointerEvents) return StrCast(doc.pointerEvents);
             if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents);
             if (props?.pointerEvents?.() === 'none') return 'none';
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bd74c9399..5100d8d67 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -79,7 +79,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc))
                 .filter(pair => {
                     // filter out any documents that have a proto that we don't have permissions to
-                    return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));
+                    return !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));
                 });
             return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4f81af95d..e7d1eeb90 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -319,6 +319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     getView = async (doc: Doc): Promise<Opt<DocumentView>> => {
         return new Promise<Opt<DocumentView>>(res => {
+            doc.hidden && (doc.hidden = false);
             const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv));
             findDoc(dv => res(dv));
         });
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index ad31113a2..f5d3243f4 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -40,7 +40,6 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate',
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
-    private _ref: HTMLDivElement | null = null;
     private _closestDropIndex: number = 0;
     private _previewRef: HTMLDivElement | null = null;
     private _makeNewColumn: boolean = false;
@@ -177,18 +176,6 @@ export class CollectionSchemaView extends CollectionSubView() {
     setSort = (field: string | undefined, desc: boolean = false) => {
         this.layoutDoc.sortField = field;
         this.layoutDoc.sortDesc = desc;
-
-        if (field === undefined) return;
-
-        this.childDocs.sort((docA, docB) => {
-            const aStr = Field.toString(docA[field] as Field);
-            const bStr = Field.toString(docB[field] as Field);
-            var out = 0;
-            if (aStr < bStr) out = -1;
-            if (aStr > bStr) out = 1;
-            if (desc) out *= -1;
-            return out;
-        });
     };
 
     addRow = (doc: Doc | Doc[]) => {
@@ -372,10 +359,9 @@ export class CollectionSchemaView extends CollectionSubView() {
         const draggedDocs = de.complete.docDragData?.draggedDocuments;
         if (draggedDocs && super.onInternalDrop(e, de)) {
             const pushedDocs = this.childDocs.filter((doc, index) => index >= this._closestDropIndex && !draggedDocs.includes(doc));
-            this.props.removeDocument?.(pushedDocs);
-            this.props.removeDocument?.(draggedDocs);
-            this.addDocument(draggedDocs);
-            this.addDocument(pushedDocs);
+            const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs];
+            const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc));
+            this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...removed, ...draggedDocs, ...pushedDocs]);
             this.setSort(undefined);
             SelectionManager.DeselectAll();
             setTimeout(() => draggedDocs.forEach(doc => DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true))), 100);
@@ -804,16 +790,30 @@ export class CollectionSchemaView extends CollectionSubView() {
         );
     }
 
+    @computed get sortedDocs() {
+        const field = StrCast(this.layoutDoc.sortField);
+        const desc = BoolCast(this.layoutDoc.sortDesc);
+        return !field
+            ? this.childDocs
+            : this.childDocs.sort((docA, docB) => {
+                  const aStr = Field.toString(docA[field] as Field);
+                  const bStr = Field.toString(docB[field] as Field);
+                  var out = 0;
+                  if (aStr < bStr) out = -1;
+                  if (aStr > bStr) out = 1;
+                  if (desc) out *= -1;
+                  return out;
+              });
+    }
+    sortedDocsFunc = () => this.sortedDocs;
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
     previewWidthFunc = () => this.previewWidth;
     render() {
-        trace();
         return (
             <div
                 className="collectionSchemaView"
                 ref={(ele: HTMLDivElement | null) => {
-                    this._ref = ele;
                     this.createDashEventsTarget(ele);
                 }}
                 onPointerDown={e => {
@@ -834,28 +834,26 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 <FontAwesomeIcon icon="plus" />
                             </div>
                         </div>
-                        {this.columnKeys.map((key, index) => {
-                            return (
-                                <SchemaColumnHeader
-                                    key={index}
-                                    columnIndex={index}
-                                    columnKeys={this.columnKeys}
-                                    columnWidths={this.displayColumnWidths}
-                                    sortField={this.sortField}
-                                    sortDesc={this.sortDesc}
-                                    setSort={this.setSort}
-                                    removeColumn={this.removeColumn}
-                                    resizeColumn={this.startResize}
-                                    openContextMenu={this.openContextMenu}
-                                    dragColumn={this.dragColumn}
-                                    setColRef={this.setColRef}
-                                />
-                            );
-                        })}
+                        {this.columnKeys.map((key, index) => (
+                            <SchemaColumnHeader
+                                key={index}
+                                columnIndex={index}
+                                columnKeys={this.columnKeys}
+                                columnWidths={this.displayColumnWidths}
+                                sortField={this.sortField}
+                                sortDesc={this.sortDesc}
+                                setSort={this.setSort}
+                                removeColumn={this.removeColumn}
+                                resizeColumn={this.startResize}
+                                openContextMenu={this.openContextMenu}
+                                dragColumn={this.dragColumn}
+                                setColRef={this.setColRef}
+                            />
+                        ))}
                     </div>
                     {this._columnMenuIndex !== undefined && this.renderColumnMenu}
                     {this._filterColumnIndex !== undefined && this.renderFilterMenu}
-                    <CollectionSchemaViewDocs schema={this} />
+                    <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} sortField={StrCast(this.layoutDoc.sortField)} sortDesc={BoolCast(this.layoutDoc.sortDesc)} />
 
                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
                 </div>
@@ -901,6 +899,9 @@ export class CollectionSchemaView extends CollectionSubView() {
 
 interface CollectionSchemaViewDocsProps {
     schema: CollectionSchemaView;
+    childDocs: () => Doc[];
+    sortField: string; // I don't know why these are needed since the childDocs function changes when the sort changes.  However, for some reason that doesn't cause a re-render...
+    sortDesc: boolean;
 }
 
 @observer
@@ -910,8 +911,8 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
     render() {
         return (
             <div className="schema-table-content">
-                {this.props.schema.childDocs.map((doc: Doc, index: number) => {
-                    const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.schema.props.DataDoc;
+                {this.props.childDocs().map((doc: Doc, index: number) => {
+                    const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.schema.props.DataDoc;
                     return (
                         <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
                             <DocumentView
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 0c8c0ee59..10f307068 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -50,7 +50,6 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     onPointerEnter = (e: any) => {
-        //if (!this.schemaView?._isDragging) return;
         if (!SnappingManager.GetIsDragging()) return;
         document.removeEventListener('pointermove', this.onPointerMove);
         document.addEventListener('pointermove', this.onPointerMove);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1f717932e..42c2b28ba 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -332,7 +332,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this.props.NativeHeight();
     }
     @computed get onClickHandler() {
-        const noClick = this.rootSelected() || this.props.isSelected() || this._componentView?.isAnyChildContentActive?.();
+        const noClick = this.props.LayoutTemplateString?.includes(KeyValueBox.name) || this.rootSelected() || this.props.isSelected() || this._componentView?.isAnyChildContentActive?.();
         return noClick ? undefined : this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
     }
     @computed get onDoubleClickHandler() {
@@ -1483,7 +1483,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         });
 
         const animRenderDoc = DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[AnimationSym], this.rootDoc);
-        return (
+        return this.hidden ? null : (
             <div
                 className={`${DocumentView.ROOT_DIV} docView-hack`}
                 ref={this._mainCont}
@@ -1640,6 +1640,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         const hideCount = this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton;
         return hideCount ? null : <DocumentLinksButton View={this} scaling={this.linkButtonInverseScaling} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
     }
+    @computed get hidden() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden);
+    }
 
     @computed get docViewPath(): DocumentView[] {
         return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this];
@@ -1826,7 +1829,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         const yshift = Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined;
         const isButton = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
 
-        return (
+        return this.hidden ? null : (
             <div
                 className="contentFittingDocumentView"
                 onPointerEnter={action(() => {
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 6e0b4be37..916458dfd 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -131,6 +131,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
                     style={{
                         backgroundColor: this.hoverColor,
                         fontSize: StrCast(this.layoutDoc._fontSize),
+                        color: StrCast(this.layoutDoc._color),
                         fontFamily: StrCast(this.layoutDoc._fontFamily) || 'inherit',
                         letterSpacing: StrCast(this.layoutDoc.letterSpacing),
                         textTransform: StrCast(this.layoutDoc.textTransform) as any,
-- 
cgit v1.2.3-70-g09d2


From dabac69d26c8e8e69bd55466ce221d9e33e36638 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 28 Mar 2023 14:23:17 -0400
Subject: numerous changes to try to simplify event handling in DocumentView -
 got rid of isContentActive in DocComponent since it's in DocumentView.  
 Including adding 'enableDragWhenActive' , 'onClickScriptDisable',

---
 src/Utils.ts                                       |  17 +-
 src/client/documents/Documents.ts                  |   3 +-
 src/client/util/CurrentUserUtils.ts                |  12 +-
 src/client/views/DocComponent.tsx                  |  13 --
 src/client/views/DocumentDecorations.tsx           |   8 +-
 src/client/views/InkingStroke.tsx                  |   2 +-
 src/client/views/MainView.tsx                      |   8 +-
 src/client/views/StyleProvider.tsx                 |   4 +-
 .../views/collections/CollectionStackingView.tsx   |   2 +
 src/client/views/collections/TabDocView.tsx        |   4 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 100 +--------
 .../collectionLinear/CollectionLinearView.scss     |  10 +-
 .../collectionLinear/CollectionLinearView.tsx      |  18 +-
 .../collectionSchema/CollectionSchemaView.scss     |   3 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  46 ++--
 .../collections/collectionSchema/SchemaRowBox.tsx  |   8 +-
 src/client/views/global/globalCssVariables.scss    |   3 +-
 .../views/global/globalCssVariables.scss.d.ts      |   2 +-
 src/client/views/nodes/ColorBox.tsx                |   2 +-
 src/client/views/nodes/DocumentView.tsx            | 238 ++++-----------------
 src/client/views/nodes/FunctionPlotBox.tsx         |   2 +-
 src/client/views/nodes/KeyValueBox.tsx             |  15 +-
 src/client/views/nodes/KeyValuePair.tsx            |   9 +-
 src/client/views/nodes/LinkBox.tsx                 |   9 +-
 src/client/views/nodes/ScriptingBox.tsx            |   4 +-
 src/client/views/nodes/WebBox.tsx                  |   2 +-
 src/client/views/nodes/button/FontIconBox.scss     |  10 +-
 src/client/views/nodes/button/FontIconBox.tsx      |   6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   9 +-
 src/client/views/nodes/trails/PresBox.tsx          |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |   9 -
 src/fields/Doc.ts                                  |   2 +-
 src/fields/ScriptField.ts                          |   2 +-
 34 files changed, 171 insertions(+), 421 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index ae1478943..0c7deaf5d 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -8,8 +8,12 @@ import { Message } from './server/Message';
 import Color = require('color');
 
 export namespace Utils {
+    export let CLICK_TIME = 300;
     export let DRAG_THRESHOLD = 4;
     export let SNAP_THRESHOLD = 10;
+    export function isClick(x: number, y: number, downX: number, downY: number, downTime: number) {
+        return Date.now() - downTime < Utils.CLICK_TIME && Math.abs(x - downX) < Utils.DRAG_THRESHOLD && Math.abs(y - downY) < Utils.DRAG_THRESHOLD;
+    }
 
     export function readUploadedFileAsText(inputFile: File) {
         const temporaryFileReader = new FileReader();
@@ -509,11 +513,22 @@ export function returnTrue() {
     return true;
 }
 
+export function returnAlways(): 'always' {
+    return 'always';
+}
+export function returnNever(): 'never' {
+    return 'never';
+}
+
+export function returnDefault(): 'default' {
+    return 'default';
+}
+
 export function returnFalse() {
     return false;
 }
 
-export function returnAll() {
+export function returnAll(): 'all' {
     return 'all';
 }
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2bc1b5b1d..c484db0db 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -230,6 +230,7 @@ export class DocumentOptions {
     contextMenuIcons?: List<string>;
     defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
     waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
+    enableDragWhenActive?: boolean;
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
     description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
@@ -587,7 +588,7 @@ export namespace Docs {
                 DocumentType.FONTICON,
                 {
                     layout: { view: FontIconBox, dataField: 'icon' },
-                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', hideLinkButton: true, _width: 40, _height: 40 },
+                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
             [
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 814b7b072..b21f53221 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -62,7 +62,7 @@ export class CurrentUserUtils {
         const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [
             {
                 btnOpts: { title: "slide", icon: "address-card" }, 
-                templateOpts: { _width: 400, _height: 300, title: "slideView", childDocumentsActive: true, _xMargin: 3, _yMargin: 3, system: true }, 
+                templateOpts: { _width: 400, _height: 300, title: "slideView",  _xMargin: 3, _yMargin: 3, system: true }, 
                 template: (opts:DocumentOptions) => Docs.Create.MultirowDocument(
                     [
                         Docs.Create.MulticolumnDocument([], { title: "data", _height: 200,  system: true }),
@@ -95,7 +95,7 @@ export class CurrentUserUtils {
 
         const reqdOpts:DocumentOptions = {
             title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
-            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true, childDocumentsActive: true,
+            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
             _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
@@ -184,10 +184,10 @@ export class CurrentUserUtils {
                 case DocumentType.IMG : creator = imageBox; break;
                 case DocumentType.FONTICON: creator = fontBox; break;
             }
-            const allopts = {system: true, ...opts};
+            const allopts = {system: true, onClickScriptDisable: "never",  ...opts};
             return DocUtils.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
                       DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
-                {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)"});
+                {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
         };
         const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
             textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
@@ -314,7 +314,7 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
             const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, 
-                _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, 
+                _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, 
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
@@ -324,7 +324,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true,
             _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-            childDocumentsActive: true, childDropAction: 'alias' 
+             childDropAction: 'alias' 
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 0b92fd864..9fc1487a0 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -82,12 +82,6 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
             return this.props.fieldKey;
         }
 
-        isContentActive = (outsideReaction?: boolean) =>
-            this.props.isContentActive?.() === false
-                ? false
-                : Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction)
-                ? true
-                : undefined;
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
     return Component;
@@ -135,13 +129,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
         isAnyChildContentActive = () => this._isAnyChildContentActive;
 
-        isContentActive = (outsideReaction?: boolean) =>
-            this.props.isContentActive?.() === false
-                ? false
-                : Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.isAnyChildContentActive()
-                ? true
-                : undefined;
-
         lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result;
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2d2d3c2f6..985e6f88f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -916,9 +916,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                         pointerEvents: 'none',
                                     }}>
                                     {this._isRotating ? null : (
-                                        <div className="documentDecorations-rotation" style={{ pointerEvents: 'all' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
-                                            <IconButton icon={<FaUndo />} isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} />
-                                        </div>
+                                        <Tooltip enterDelay={750} title={<div className="dash-tooltip">tap to set rotate center, drag to rotate</div>}>
+                                            <div className="documentDecorations-rotation" style={{ pointerEvents: 'all' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
+                                                <IconButton icon={<FaUndo />} isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} />
+                                            </div>
+                                        </Tooltip>
                                     )}
                                 </div>
                                 {!this._showRotCenter ? null : (
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index a085b69a5..e3642fdaf 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -473,7 +473,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                             dontRegisterView={true}
                             noSidebar={true}
                             dontScale={true}
-                            isContentActive={this.isContentActive}
+                            isContentActive={this.props.isContentActive}
                         />
                     </div>
                 )}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c84d204d5..f5adc17d0 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -40,7 +40,7 @@ import { DashboardView } from './DashboardView';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
-import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
+import { TOPBAR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
 import { Colors } from './global/globalEnums';
 import { KeyManager } from './GlobalKeyHandler';
 import { InkTranscription } from './InkTranscription';
@@ -86,7 +86,7 @@ export class MainView extends React.Component {
         return this._hideUI ? 0 : 27;
     } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
     @computed private get topOfDashUI() {
-        return this._hideUI || LightboxView.LightboxDoc ? 0 : Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', ''));
+        return this._hideUI || LightboxView.LightboxDoc ? 0 : Number(TOPBAR_HEIGHT.replace('px', ''));
     }
     @computed private get topOfHeaderBarDoc() {
         return this.topOfDashUI;
@@ -872,7 +872,7 @@ export class MainView extends React.Component {
 
     @computed get docButtons() {
         return !Doc.MyDockedBtns ? null : (
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !Doc.MyDockedBtns.linearViewIsExpanded ? '42px' : undefined }}>
+            <div className="mainView-docButtons" ref={this._docBtnRef}>
                 <CollectionLinearView
                     Document={Doc.MyDockedBtns}
                     DataDoc={undefined}
@@ -910,7 +910,7 @@ export class MainView extends React.Component {
         );
     }
     @computed get snapLines() {
-        return !SnappingManager.GetShowSnapLines() ? null : (
+        return !SelectionManager.Views().some(dv => dv.rootDoc.showSnapLines) ? null : (
             <div className="mainView-snapLines">
                 <svg style={{ width: '100%', height: '100%' }}>
                     {SnappingManager.horizSnapLines().map(l => (
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 1b5eb3342..faaa4e1f9 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -89,6 +89,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isAnchor = property.includes(':anchor');
     const isAnnotated = property.includes(':annotated');
     const isOpen = property.includes(':open');
+    const boxBackground = property.includes(':box');
     const fieldKey = props?.fieldKey ? props.fieldKey + '-' : isCaption ? 'caption-' : '';
     const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
@@ -200,7 +201,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     docColor = docColor || (darkScheme() ? 'transparent' : 'transparent');
                     break;
                 case DocumentType.FONTICON:
-                    docColor = docColor || Colors.DARK_GRAY;
+                    docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY;
                     break;
                 case DocumentType.RTF:
                     docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY);
@@ -289,6 +290,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         }
         case StyleProp.PointerEvents:
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
+            if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.();
             if (MainView.Instance._exploreMode || doc?.unrendered) return isInk ? 'visiblePainted' : 'all';
             if (doc?.pointerEvents) return StrCast(doc.pointerEvents);
             if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 33d468950..64ec419ec 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -325,6 +325,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 fitWidth={this.props.childFitWidth}
                 isContentActive={doc.isLinkButton ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
+                onBrowseClick={this.props.onBrowseClick}
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this.props.childLayoutTemplate}
                 LayoutTemplateString={this.props.childLayoutString}
@@ -659,6 +660,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         addDocument={this.props.addDocument}
                         moveDocument={this.props.moveDocument}
                         addDocTab={this.props.addDocTab}
+                        onBrowseClick={this.props.onBrowseClick}
                         pinToPres={emptyFunction}
                         rootSelected={this.props.isSelected}
                         removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5e6849e7c..93b816581 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -10,7 +10,6 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
 import { listSpec } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
@@ -28,6 +27,7 @@ import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
 import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
 import { DashFieldView } from '../nodes/formattedText/DashFieldView';
+import { KeyValueBox } from '../nodes/KeyValueBox';
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
@@ -35,7 +35,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import React = require('react');
-import { KeyValueBox } from '../nodes/KeyValueBox';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -429,6 +428,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     ContainingCollectionDoc={undefined}
                     onBrowseClick={MainView.Instance.exploreMode}
                     isContentActive={returnTrue}
+                    isDocumentActive={returnFalse}
                     PanelWidth={this.PanelWidth}
                     PanelHeight={this.PanelHeight}
                     styleProvider={DefaultStyleProvider}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 257428d56..99b7549c0 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -573,6 +573,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         </div>
                     )}
                     <ul
+                        style={{ cursor: 'inherit' }}
                         key={expandKey + 'more'}
                         title="click to change sort order"
                         className={''} //this.doc.treeViewHideTitle ? 'no-indent' : ''}
@@ -629,7 +630,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             );
         } else if (this.treeViewExpandedView === 'fields') {
             return (
-                <ul key={this.doc[Id] + this.doc.title}>
+                <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit' }}>
                     <div>{this.expandedField}</div>
                 </ul>
             );
@@ -903,7 +904,8 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
-                onClickScriptDisable="never"
+                enableDragWhenActive={true}
+                onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
                 docViewPath={returnEmptyDoclist}
                 treeViewDoc={this.props.treeView.props.Document}
                 addDocument={undefined}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8104ab1a7..aed3683d4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -821,9 +821,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             Doc.ActiveTool = InkTool.None;
-            if (this.props.isContentActive(true)) e.stopPropagation();
         } else {
-            this.props.DocumentView?.().docView?.cancelMoveEvents();
             if (this.tryDragCluster(e, this._hitCluster)) {
                 return true;
             }
@@ -955,96 +953,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return tVals;
     };
 
-    handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-        if (!e.cancelBubble) {
-            const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
-            if (myTouches[0]) {
-                if (Doc.ActiveTool === InkTool.None) {
-                    if (this.tryDragCluster(e, this._hitCluster)) {
-                        e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
-                        e.preventDefault();
-                        document.removeEventListener('pointermove', this.onPointerMove);
-                        return;
-                    }
-                    // TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected'
-                    this.pan(myTouches[0]);
-                }
-            }
-            // e.stopPropagation();
-            e.preventDefault();
-        }
-    };
-
-    handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-        // pinch zooming
-        if (!e.cancelBubble) {
-            const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
-            const pt1 = myTouches[0];
-            const pt2 = myTouches[1];
-
-            if (this.prevPoints.size === 2) {
-                const oldPoint1 = this.prevPoints.get(pt1.identifier);
-                const oldPoint2 = this.prevPoints.get(pt2.identifier);
-                if (oldPoint1 && oldPoint2) {
-                    const dir = InteractionUtils.Pinching(pt1, pt2, oldPoint1, oldPoint2);
-
-                    // if zooming, zoom
-                    if (dir !== 0) {
-                        const d1 = Math.sqrt(Math.pow(pt1.clientX - oldPoint1.clientX, 2) + Math.pow(pt1.clientY - oldPoint1.clientY, 2));
-                        const d2 = Math.sqrt(Math.pow(pt2.clientX - oldPoint2.clientX, 2) + Math.pow(pt2.clientY - oldPoint2.clientY, 2));
-                        const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
-                        const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
-
-                        // calculate the raw delta value
-                        const rawDelta = dir * (d1 + d2);
-
-                        // this floors and ceils the delta value to prevent jitteriness
-                        const delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 8);
-                        this.zoom(centerX, centerY, delta * window.devicePixelRatio);
-                        this.prevPoints.set(pt1.identifier, pt1);
-                        this.prevPoints.set(pt2.identifier, pt2);
-                    }
-                    // this is not zooming. derive some form of panning from it.
-                    else {
-                        // use the centerx and centery as the "new mouse position"
-                        const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
-                        const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
-                        // const transformed = this.getTransform().inverse().transformPoint(centerX, centerY);
-
-                        this._lastX = centerX;
-                        this._lastY = centerY;
-                    }
-                }
-            }
-            // e.stopPropagation();
-            e.preventDefault();
-        }
-    };
-
-    @action
-    handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
-        if (this.props.isContentActive(true)) {
-            // const pt1: React.Touch | null = e.targetTouches.item(0);
-            // const pt2: React.Touch | null = e.targetTouches.item(1);
-            // // if (!pt1 || !pt2) return;
-            const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
-            const pt1 = myTouches[0];
-            const pt2 = myTouches[1];
-            if (pt1 && pt2) {
-                const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
-                const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
-                this._lastX = centerX;
-                this._lastY = centerY;
-
-                this.removeMoveListeners();
-                this.addMoveListeners();
-                this.removeEndListeners();
-                this.addEndListeners();
-                e.stopPropagation();
-            }
-        }
-    };
-
     cleanUpInteractions = () => {
         this.removeMoveListeners();
         this.removeEndListeners();
@@ -1637,7 +1545,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             } else {
                 const canvas = oldDiv;
                 const img = document.createElement('img'); // create a Image Element
-                img.src = canvas.toDataURL(); //image source
+                try {
+                    img.src = canvas.toDataURL(); //image source
+                } catch (e) {
+                    console.log(e);
+                }
                 img.style.width = canvas.style.width;
                 img.style.height = canvas.style.height;
                 const newCan = newDiv as HTMLCanvasElement;
@@ -2265,6 +2177,8 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
         const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it.  Otherwise, zoom out on the parent ffview
         ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime);
         Doc.linkFollowHighlight(dv?.props.Document, false);
+    } else {
+        DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true });
     }
 }
 ScriptingGlobals.add(CollectionBrowseClick);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
index 521bcda1e..3e3709827 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
@@ -1,9 +1,13 @@
 @import '../../global/globalCssVariables';
 @import '../../_nodeModuleOverrides';
 
+.collectionLinearView {
+    width: 100%;
+}
 .collectionLinearView-label {
     color: black;
     background-color: $light-gray;
+    width: 100%;
 }
 .collectionLinearView-outer {
     overflow: visible;
@@ -15,8 +19,6 @@
     }
 
     &.true {
-        padding-left: 5px;
-        padding-right: 5px;
         border-left: $standard-border;
         background-color: $medium-blue-alt;
     }
@@ -29,7 +31,6 @@
         display: flex;
         height: 100%;
         align-items: center;
-        gap: 5px;
 
         .collectionView {
             overflow: visible !important;
@@ -101,13 +102,12 @@
             background-color: $medium-blue;
             padding: 5;
             border-radius: 2px;
-            height: 25;
+            height: 100%;
             min-width: 25;
             margin: 0;
             color: $white;
             display: flex;
             font-weight: 100;
-            width: fit-content;
             transition: transform 0.2s;
             align-items: center;
             justify-content: center;
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index d54e8ce98..a062c65fc 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { StylesProvider, Tooltip } from '@material-ui/core';
+import { Tooltip } from '@material-ui/core';
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -11,8 +11,6 @@ import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
-import { Colors, Shadows } from '../../global/globalEnums';
-import { media_state } from '../../nodes/AudioBox';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
 import { DocumentView } from '../../nodes/DocumentView';
 import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
@@ -46,7 +44,7 @@ export class CollectionLinearView extends CollectionSubView() {
 
     componentDidMount() {
         this._widthDisposer = reaction(
-            () => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * this.rootDoc[HeightSym]() : 10),
+            () => 5 + this.dimension() + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[WidthSym]() || this.dimension()) + tot + 4, 0) : 0),
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
@@ -180,10 +178,10 @@ export class CollectionLinearView extends CollectionSubView() {
                 ref={r => (dref = r || undefined)}
                 style={{
                     pointerEvents: 'all',
-                    width: nested ? undefined : NumCast(doc._width),
-                    height: nested ? undefined : NumCast(doc._height),
-                    marginLeft: !nested ? 2.5 : 0,
-                    marginRight: !nested ? 2.5 : 0,
+                    width: NumCast(doc._width),
+                    height: NumCast(doc._height),
+                    marginLeft: 2,
+                    marginRight: 2,
                     // width: NumCast(pair.layout._width),
                     // height: NumCast(pair.layout._height),
                 }}>
@@ -199,7 +197,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     rootSelected={this.props.isSelected}
                     removeDocument={this.props.removeDocument}
                     ScreenToLocalTransform={docXf}
-                    PanelWidth={nested ? doc[WidthSym] : this.dimension}
+                    PanelWidth={doc[WidthSym]}
                     PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
                     renderDepth={this.props.renderDepth + 1}
                     dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
@@ -236,7 +234,7 @@ export class CollectionLinearView extends CollectionSubView() {
 
         return (
             <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: this.layoutDoc.linearViewIsExpanded ? undefined : 'transparent' }}>
-                <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu}>
+                <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension() }}>
                     {!this.props.Document.linearViewExpandable ? null : (
                         <Tooltip title={<div className="dash-tooltip">{isExpanded ? 'Close' : 'Open'}</div>} placement="top">
                             {menuOpener}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 34e591195..1ef2fb4ef 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -8,7 +8,8 @@
 
     .schema-table {
         background-color: $white;
-        cursor: default;
+        cursor: grab;
+        overflow: scroll;
 
         .schema-column-menu,
         .schema-filter-menu {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 60202a19e..d47c9762c 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -9,7 +9,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
+import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
@@ -46,7 +46,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     public static _rowHeight: number = 40;
     public static _minColWidth: number = 25;
-    public static _rowMenuWidth: number = 100;
+    public static _rowMenuWidth: number = 60;
     public static _previewDividerWidth: number = 4;
 
     @computed get _selectedDocs() {
@@ -364,7 +364,11 @@ export class CollectionSchemaView extends CollectionSubView() {
             this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...removed, ...draggedDocs, ...pushedDocs]);
             this.setSort(undefined);
             SelectionManager.DeselectAll();
-            setTimeout(() => draggedDocs.forEach(doc => DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true))), 100);
+            draggedDocs.forEach(doc => {
+                const draggedView = DocumentManager.Instance.getFirstDocumentView(doc);
+                if (draggedView) DocumentManager.Instance.RemoveView(draggedView);
+                DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true));
+            });
             e.stopPropagation();
             return true;
         }
@@ -812,26 +816,17 @@ export class CollectionSchemaView extends CollectionSubView() {
     previewWidthFunc = () => this.previewWidth;
     render() {
         return (
-            <div
-                className="collectionSchemaView"
-                ref={(ele: HTMLDivElement | null) => {
-                    this.createDashEventsTarget(ele);
-                }}
-                onPointerDown={e => {
-                    // this is analogous to the panning code for a freeform view.
-                    // however, schema views don't pan so it does nothing.  but it does eat the pointerDown event
-                    // if the content is active to prevent the schema from being dragged
-                    this.isContentActive() && setupMoveUpEvents(this, e, returnFalse, emptyFunction, emptyFunction, false);
-                }}
-                onDrop={this.onExternalDrop.bind(this)}>
-                <div className="schema-table">
+            <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}>
+                <div
+                    className="schema-table"
+                    onWheel={e => this.props.isContentActive() && e.stopPropagation()}
+                    ref={r => {
+                        // prevent wheel events from passively propagating up through containers
+                        r?.addEventListener('wheel', (e: WheelEvent) => {}, { passive: false });
+                    }}>
                     <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
                         <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}>
-                            <div
-                                className="schema-header-button"
-                                onPointerDown={e => {
-                                    this._columnMenuIndex && this._columnMenuIndex === -1 ? this.closeColumnMenu() : this.openColumnMenu(-1, true);
-                                }}>
+                            <div className="schema-header-button" onPointerDown={e => (this._columnMenuIndex === -1 ? this.closeColumnMenu() : this.openColumnMenu(-1, true))}>
                                 <FontAwesomeIcon icon="plus" />
                             </div>
                         </div>
@@ -867,8 +862,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 DataDoc={undefined}
                                 fitContentsToBox={returnTrue}
                                 dontCenter={'y'}
+                                onClickScriptDisable="always"
                                 focus={DocUtils.DefaultFocus}
-                                renderDepth={this.props.renderDepth}
+                                renderDepth={this.props.renderDepth + 1}
                                 rootSelected={this.rootSelected}
                                 PanelWidth={this.previewWidthFunc}
                                 PanelHeight={this.props.PanelHeight}
@@ -915,16 +911,22 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                     return (
                         <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
                             <DocumentView
+                                key={doc[Id]}
                                 {...this.props.schema.props}
                                 LayoutTemplate={this.props.schema.props.childLayoutTemplate}
                                 LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey)}
                                 Document={doc}
                                 DataDoc={dataDoc}
+                                renderDepth={this.props.schema.props.renderDepth + 1}
                                 ContainingCollectionView={this.props.schema.props.CollectionView}
                                 ContainingCollectionDoc={this.props.schema.Document}
                                 PanelWidth={this.tableWidthFunc}
                                 PanelHeight={this.rowHeightFunc}
                                 styleProvider={DefaultStyleProvider}
+                                waitForDoubleClickToClick={returnNever}
+                                defaultDoubleClick={returnDefault}
+                                enableDragWhenActive={true}
+                                onClickScriptDisable="always"
                                 focus={this.props.schema.focusDocument}
                                 docFilters={this.props.schema.childDocFilters}
                                 docRangeFilters={this.props.schema.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 10f307068..2defaae00 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -88,11 +88,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="schema-row"
-                style={
-                    this.props.isSelected()
-                        ? { height: CollectionSchemaView._rowHeight, backgroundColor: Colors.LIGHT_BLUE, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined /*, opacity: this.props.dragging ? 0.5 : 1 */ }
-                        : { height: CollectionSchemaView._rowHeight, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }
-                }
+                style={{ height: CollectionSchemaView._rowHeight, backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }}
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
@@ -103,7 +99,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     className="row-menu"
                     style={{
                         width: CollectionSchemaView._rowMenuWidth,
-                        pointerEvents: !this.isContentActive() ? 'none' : undefined,
+                        pointerEvents: !this.props.isContentActive() ? 'none' : undefined,
                     }}>
                     <div
                         className="schema-row-button"
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index 430a36dce..3496bb835 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -59,7 +59,6 @@ $standard-border-radius: 3px;
 // shadow
 $standard-box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
 
-$dashboardselector-height: 32px;
 $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
 $docDecorations-zindex: 998; // then doc decorations appear over everything else
 $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
@@ -79,7 +78,7 @@ $TREE_BULLET_WIDTH: 20px;
     MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
     SEARCH_THUMBNAIL_SIZE: $search-thumnail-size;
     ANTIMODEMENU_HEIGHT: $antimodemenu-height;
-    DASHBOARD_SELECTOR_HEIGHT: $dashboardselector-height;
+    TOPBAR_HEIGHT: $topbar-height;
     DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM;
     LEFT_MENU_WIDTH: $LEFT_MENU_WIDTH;
     TREE_BULLET_WIDTH: $TREE_BULLET_WIDTH;
diff --git a/src/client/views/global/globalCssVariables.scss.d.ts b/src/client/views/global/globalCssVariables.scss.d.ts
index 3375579d6..537ea1e5d 100644
--- a/src/client/views/global/globalCssVariables.scss.d.ts
+++ b/src/client/views/global/globalCssVariables.scss.d.ts
@@ -6,7 +6,7 @@ interface IGlobalScss {
     MAX_ROW_HEIGHT: string;
     SEARCH_THUMBNAIL_SIZE: string;
     ANTIMODEMENU_HEIGHT: string;
-    DASHBOARD_SELECTOR_HEIGHT: string;
+    TOPBAR_HEIGHT: string;
     DFLT_IMAGE_NATIVE_DIM: string;
     LEFT_MENU_WIDTH: string;
     TREE_BULLET_WIDTH: string;
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index c229a966a..70ba7e182 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>() {
         const scaling = Math.min(this.layoutDoc.fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]());
         return (
             <div
-                className={`colorBox-container${this.isContentActive() ? '-interactive' : ''}`}
+                className={`colorBox-container${this.props.isContentActive() ? '-interactive' : ''}`}
                 onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
                 onClick={e => e.stopPropagation()}
                 style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }}>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 541dddd83..9a3e77e6e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,7 +5,6 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from 'mobx-react';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
 import { AclAdmin, AclEdit, AclPrivate, AnimationSym, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
-import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
@@ -15,7 +14,7 @@ import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from
 import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnNone, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { DocServer } from '../../DocServer';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -50,11 +49,9 @@ import { FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { RadialMenu } from './RadialMenu';
-import { ScriptingBox } from './ScriptingBox';
 import { PresEffect, PresEffectDirection } from './trails';
 import { PinProps, PresBox } from './trails/PresBox';
 import React = require('react');
-import { KeyValueBox } from './KeyValueBox';
 const { Howl } = require('howler');
 
 interface Window {
@@ -135,6 +132,7 @@ export interface DocComponentView {
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     incrementalRendering?: () => void;
     fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
+    overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document
     fieldKey?: string;
     annotationKey?: string;
     getTitle?: () => string;
@@ -195,6 +193,9 @@ export interface DocumentViewSharedProps {
     forceAutoHeight?: boolean;
     disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
     onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
+    enableDragWhenActive?: boolean;
+    waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
+    defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
     pointerEvents?: () => Opt<string>;
     scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
     createNewFilterDoc?: () => void;
@@ -255,8 +256,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     private _downX: number = 0;
     private _downY: number = 0;
     private _downTime: number = 0;
-    private _firstX: number = -1;
-    private _firstY: number = -1;
     private _lastTap: number = 0;
     private _doubleTap = false;
     private _mainCont = React.createRef<HTMLDivElement>();
@@ -333,11 +332,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this.props.NativeHeight();
     }
     @computed get disableClickScriptFunc() {
+        const onScriptDisable = this.props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable;
+        // prettier-ignore
         return (
             DocumentView.LongPress ||
-            this.props.onClickScriptDisable === 'always' ||
-            this._componentView?.onClickScriptDisable?.() ||
-            (this.props.onClickScriptDisable !== 'never' && (this.rootSelected() || this.props.isSelected())) ||
+            onScriptDisable === 'always' ||
+            (onScriptDisable !== 'never' && (this.rootSelected() || this.props.isSelected())) ||
             this._componentView?.isAnyChildContentActive?.()
         );
     }
@@ -360,7 +360,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     componentDidMount() {
         this.setupHandlers();
     }
-    //componentDidUpdate() { this.setupHandlers(); }
+
     setupHandlers() {
         this.cleanupHandlers(false);
         if (this._mainCont.current) {
@@ -378,151 +378,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
-    handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
-        this.removeMoveListeners();
-        this.removeEndListeners();
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.removeEventListener('pointerup', this.onPointerUp);
-        if (RadialMenu.Instance._display === false) {
-            this.addHoldMoveListeners();
-            this.addHoldEndListeners();
-            this.onRadialMenu(e, me);
-            const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
-            this._firstX = pt.pageX;
-            this._firstY = pt.pageY;
-        }
-    };
-
-    handle1PointerHoldMove = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
-        const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
-
-        if (this._firstX === -1 || this._firstY === -1) {
-            return;
-        }
-        if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) {
-            this.handle1PointerHoldEnd(e, me);
-        }
-    };
-
-    handle1PointerHoldEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
-        this.removeHoldMoveListeners();
-        this.removeHoldEndListeners();
-        RadialMenu.Instance.closeMenu();
-        this._firstX = -1;
-        this._firstY = -1;
-        SelectionManager.DeselectAll();
-        me.touchEvent.stopPropagation();
-        me.touchEvent.preventDefault();
-        e.stopPropagation();
-        if (RadialMenu.Instance.used) {
-            this.onContextMenu(undefined, me.touches[0].pageX, me.touches[0].pageY);
-        }
-    };
-
-    handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
-        if (!this.props.isSelected()) {
-            e.stopPropagation();
-            e.preventDefault();
-
-            this.removeMoveListeners();
-            this.addMoveListeners();
-            this.removeEndListeners();
-            this.addEndListeners();
-        }
-    };
-
-    handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
-        SelectionManager.DeselectAll();
-        if (this.Document.onPointerDown) return;
-        const touch = me.touchEvent.changedTouches.item(0);
-        if (touch) {
-            this._downX = touch.clientX;
-            this._downY = touch.clientY;
-            if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
-                e.stopPropagation();
-            }
-            this.removeMoveListeners();
-            this.addMoveListeners();
-            this.removeEndListeners();
-            this.addEndListeners();
-            e.stopPropagation();
-        }
-    };
-
-    handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-        if (e.cancelBubble && this.props.isDocumentActive?.()) {
-            this.removeMoveListeners();
-        } else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
-            const touch = me.touchEvent.changedTouches.item(0);
-            if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
-                if (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) {
-                    this.cleanUpInteractions();
-                    this.startDragging(this._downX, this._downY, this.Document.dropAction ? (this.Document.dropAction as any) : e.ctrlKey || e.altKey ? 'alias' : undefined);
-                }
-            }
-            e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
-            e.preventDefault();
-        }
-    };
-
-    @action
-    handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-        const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
-        const pt1 = myTouches[0];
-        const pt2 = myTouches[1];
-        const oldPoint1 = this.prevPoints.get(pt1.identifier);
-        const oldPoint2 = this.prevPoints.get(pt2.identifier);
-        const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!);
-        if (pinching !== 0 && oldPoint1 && oldPoint2) {
-            const dW = Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX);
-            const dH = Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY);
-            const dX = -1 * Math.sign(dW);
-            const dY = -1 * Math.sign(dH);
-
-            if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                const doc = Document(this.props.Document);
-                const layoutDoc = Document(Doc.Layout(this.props.Document));
-                let nwidth = Doc.NativeWidth(layoutDoc);
-                let nheight = Doc.NativeHeight(layoutDoc);
-                const width = layoutDoc._width || 0;
-                const height = layoutDoc._height || (nheight / nwidth) * width;
-                const scale = this.props.ScreenToLocalTransform().Scale * this.NativeDimScaling;
-                const actualdW = Math.max(width + dW * scale, 20);
-                const actualdH = Math.max(height + dH * scale, 20);
-                doc.x = (doc.x || 0) + dX * (actualdW - width);
-                doc.y = (doc.y || 0) + dY * (actualdH - height);
-                const fixedAspect = e.ctrlKey || (nwidth && nheight);
-                if (fixedAspect && (!nwidth || !nheight)) {
-                    Doc.SetNativeWidth(layoutDoc, (nwidth = layoutDoc._width || 0));
-                    Doc.SetNativeHeight(layoutDoc, (nheight = layoutDoc._height || 0));
-                }
-                if (nwidth > 0 && nheight > 0) {
-                    if (Math.abs(dW) > Math.abs(dH)) {
-                        if (!fixedAspect) {
-                            Doc.SetNativeWidth(layoutDoc, (actualdW / (layoutDoc._width || 1)) * Doc.NativeWidth(layoutDoc));
-                        }
-                        layoutDoc._width = actualdW;
-                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._height = (nheight / nwidth) * layoutDoc._width;
-                        else layoutDoc._height = actualdH;
-                    } else {
-                        if (!fixedAspect) {
-                            Doc.SetNativeHeight(layoutDoc, (actualdH / (layoutDoc._height || 1)) * Doc.NativeHeight(doc));
-                        }
-                        layoutDoc._height = actualdH;
-                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._width = (nwidth / nheight) * layoutDoc._height;
-                        else layoutDoc._width = actualdW;
-                    }
-                } else {
-                    dW && (layoutDoc._width = actualdW);
-                    dH && (layoutDoc._height = actualdH);
-                    dH && layoutDoc._autoHeight && (layoutDoc._autoHeight = false);
-                }
-            }
-            e.stopPropagation();
-            e.preventDefault();
-        }
-    };
-
     @action
     onRadialMenu = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): void => {
         const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
@@ -591,11 +446,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     };
 
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
-        if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+        if (!this.Document.ignoreClick && this.pointerEvents !== 'none' && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
             let stopPropagate = true;
             let preventDefault = true;
             (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap) {
+                const defaultDblclick = this.props.defaultDoubleClick?.() || this.Document.defaultDoubleClick;
                 if (this.onDoubleClickHandler?.script) {
                     const { clientX, clientY, shiftKey, altKey, ctrlKey } = e; // or we could call e.persist() to capture variables
                     // prettier-ignore
@@ -609,7 +465,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             value: undefined,
                         }, console.log  );
                     UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
-                } else if (!Doc.IsSystem(this.rootDoc) && (this.Document.defaultDoubleClick === undefined || this.Document.defaultDoubleClick === 'default')) {
+                } else if (!Doc.IsSystem(this.rootDoc) && (defaultDblclick === undefined || defaultDblclick === 'default')) {
                     UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
@@ -649,15 +505,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         LinkFollower.FollowLink(undefined, this.Document, e.altKey);
                     };
                 } else {
+                    // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
-                        // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                         stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
                     }
                     preventDefault = false;
                 }
 
                 this._singleClickFunc = clickFunc ?? (() => (this._componentView?.select ?? this.props.select)(e.ctrlKey || e.metaKey, e.shiftKey));
-                if ((clickFunc && this.Document.waitForDoubleClickToClick !== 'never') || this.Document.waitForDoubleClickToClick === 'always') {
+                const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
+                if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') {
                     this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
                     this._doubleClickTimeout = setTimeout(this._singleClickFunc, 300);
                 } else {
@@ -673,55 +530,46 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         this._longPressSelector = setTimeout(() => DocumentView.LongPress && this.props.select(false), 1000);
-        if (!(e.nativeEvent as any).DownDocView) (e.nativeEvent as any).DownDocView = GestureOverlay.DownDocView = this.props.DocumentView();
-        if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return;
-        // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
-        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool))) {
-            if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                e.stopPropagation();
-                if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
-                // TODO: check here for panning/inking
-            }
-            return;
-        }
+        if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this.props.DocumentView();
+
         this._downX = e.clientX;
         this._downY = e.clientY;
         this._downTime = Date.now();
         if ((Doc.ActiveTool === InkTool.None || this.props.addDocTab === returnFalse) && !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
-            // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
+            // click events stop here if the document is active and no modes are overriding it
+            // if this is part of a template, let the event go up to the template root unless right/ctrl clicking
             if (
-                (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
+                // prettier-ignore
+                this.props.isDocumentActive?.() &&
                 !this.props.onBrowseClick?.() &&
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
-                (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
+                e.button === 0 &&
+                this.pointerEvents !== 'none' &&
                 !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)
             ) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
+
+                // listen to move events if document content isn't active or document is draggable
+                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || this.props.enableDragWhenActive || this.rootDoc.enableDragWhenActive)) {
+                    document.addEventListener('pointermove', this.onPointerMove);
+                }
             }
-            if (this.props.isDocumentActive?.()) {
-                document.removeEventListener('pointermove', this.onPointerMove);
-                document.addEventListener('pointermove', this.onPointerMove);
-            }
-            document.removeEventListener('pointerup', this.onPointerUp);
             document.addEventListener('pointerup', this.onPointerUp);
         }
     };
 
     @action
     onPointerMove = (e: PointerEvent): void => {
-        if (this.layoutDoc._lockedPosition || DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
 
-        if (((!this.topMost && this.props.isDocumentActive?.()) || this.layoutDoc.onDragStart) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
+        if (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 this.cleanupPointerEvents();
                 this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
             }
-            e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
-            e.preventDefault();
         }
     };
 
@@ -738,13 +586,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         this.cleanupPointerEvents();
         this._longPressSelector && clearTimeout(this._longPressSelector);
 
-        const now = Date.now();
         if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
             this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
-        } else if (now - this._downTime < 300) {
-            this._doubleTap = now - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2;
-            // bcz: this is a placeholder.  documents, when selected, should stopPropagation on doubleClicks if they want to keep the DocumentView from getting them
-            if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
+        } else if (e.button === 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
+            this._doubleTap = Date.now() - this._lastTap < Utils.CLICK_TIME;
+            if (!this.props.isSelected(true)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
         }
         if (DocumentView.LongPress) e.preventDefault();
     };
@@ -1386,11 +1232,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>
         );
     }
-    @observable _: string = '';
+
     renderDoc = (style: object) => {
         TraceMobx();
         const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png');
-        const isButton = this.props.Document.type === DocumentType.FONTICON;
+        const background = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box');
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
         return (
             this.docContents ?? (
@@ -1399,7 +1245,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     id={this.props.Document[Id]}
                     style={{
                         ...style,
-                        background: isButton || thumb ? undefined : this.backgroundColor,
+                        background: thumb ? undefined : background,
                         opacity: this.opacity,
                         cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
                         color: StrCast(this.layoutDoc.color, 'inherit'),
@@ -1809,7 +1655,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         TraceMobx();
         const xshift = Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined;
         const yshift = Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined;
-        const isButton = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
 
         return this.hidden ? null : (
             <div
@@ -1831,12 +1676,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         ref={this.ContentRef}
                         style={{
                             transition: this.props.dataTransition,
-                            transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                            width: isButton ? '100%' : xshift ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
-                            height:
-                                isButton || this.props.LayoutTemplateString?.includes(KeyValueBox.name) || this.props.forceAutoHeight
-                                    ? undefined
-                                    : yshift ?? (this.fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
+                            transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
+                            width: xshift ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
+                            height: this.props.forceAutoHeight
+                                ? undefined
+                                : yshift ?? (this.fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
                         }}>
                         <DocumentViewInternal
                             {...this.props}
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 75b4907cd..b43e359ff 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -101,7 +101,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
             <div
                 ref={this.createDropTarget}
                 style={{
-                    pointerEvents: !this.isContentActive() ? 'all' : undefined,
+                    pointerEvents: !this.props.isContentActive() ? 'all' : undefined,
                     width: this.props.PanelWidth(),
                     height: this.props.PanelHeight(),
                 }}>
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 9c31ed3e4..57018fb93 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -4,8 +4,9 @@ import { Doc, Field, FieldResult } from '../../../fields/Doc';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DocCast, NumCast } from '../../../fields/Types';
+import { DocCast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
+import { returnAll, returnAlways, returnTrue } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { SetupDrag } from '../../util/DragManager';
 import { CompiledScript, CompileScript, ScriptOptions } from '../../util/Scripting';
@@ -20,7 +21,6 @@ import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import React = require('react');
 import e = require('express');
-import { returnTrue } from '../../../Utils';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -42,8 +42,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     componentDidMount() {
         this.props.setContentView?.(this);
     }
-    onClickScriptDisable: () => 'always' = () => 'always';
+    reverseNativeScaling = returnTrue;
+    able = returnAlways;
     fitWidth = returnTrue;
+    overridePointerEvents = returnAll;
+    onClickScriptDisable = returnAlways;
 
     @observable private rows: KeyValuePair[] = [];
 
@@ -69,9 +72,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     };
     public static CompileKVPScript(value: string): KVPScript | undefined {
         const eq = value.startsWith('=');
-        value = eq ? value.substr(1) : value;
-        const dubEq = value.startsWith(':=') ? 'computed' : value.startsWith(';=') ? 'script' : false;
-        value = dubEq ? value.substr(2) : value;
+        value = eq ? value.substring(1) : value;
+        const dubEq = value.startsWith(':=') ? 'computed' : value.startsWith('$=') ? 'script' : false;
+        value = dubEq ? value.substring(2) : value;
         const options: ScriptOptions = { addReturn: true, typecheck: false, params: { this: Doc.name, self: Doc.name, _last_: 'any', _readOnly_: 'boolean' }, editable: false };
         if (dubEq) options.typecheck = false;
         const script = CompileScript(value, options);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index c4adc7f1a..94434dce7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,19 +1,18 @@
 import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, Field, Opt } from '../../../fields/Doc';
-import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter, returnEmptyDoclist, emptyPath } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
+import { Doc, Field } from '../../../fields/Doc';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../Utils';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { EditableView } from '../EditableView';
+import { DefaultStyleProvider } from '../StyleProvider';
+import { OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { KeyValueBox } from './KeyValueBox';
 import './KeyValueBox.scss';
 import './KeyValuePair.scss';
 import React = require('react');
-import { DefaultStyleProvider } from '../StyleProvider';
-import { OpenWhere } from './DocumentView';
 
 // Represents one row in a key value plane
 
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 470f7e803..46ccdecae 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,6 +1,6 @@
 import React = require('react');
 import { observer } from 'mobx-react';
-import { emptyFunction, returnFalse, returnTrue } from '../../../Utils';
+import { emptyFunction, returnAlways, returnFalse, returnTrue } from '../../../Utils';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import { ComparisonBox } from './ComparisonBox';
@@ -12,23 +12,22 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(LinkBox, fieldKey);
     }
-    isContentActiveFunc = () => this.isContentActive();
 
-    onClickScriptDisable: () => 'always' = () => 'always';
+    onClickScriptDisable = returnAlways;
     componentDidMount() {
         this.props.setContentView?.(this);
     }
     render() {
         if (this.dataDoc.treeViewOpen === undefined) setTimeout(() => (this.dataDoc.treeViewOpen = true));
         return (
-            <div className={`linkBox-container${this.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
+            <div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
                 <ComparisonBox
                     {...this.props}
                     fieldKey="anchor"
                     setHeight={emptyFunction}
                     dontRegisterView={true}
                     renderDepth={this.props.renderDepth + 1}
-                    isContentActive={this.isContentActiveFunc}
+                    isContentActive={this.props.isContentActive}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     moveDocument={returnFalse}
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 5683b0fe7..f09962b22 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -8,7 +8,7 @@ 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 { returnEmptyString, returnTrue } from '../../../Utils';
+import { returnAlways, returnEmptyString, returnTrue } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { CompileScript, ScriptParam } from '../../util/Scripting';
@@ -114,7 +114,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         }
     }
 
-    onClickScriptDisable: () => 'always' = () => 'always';
+    onClickScriptDisable = returnAlways;
 
     @action
     componentDidMount() {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 73283263f..d57518a8d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -876,7 +876,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
     };
     @computed get content() {
-        const interactive = this.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
+        const interactive = this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
         return (
             <div className={'webBox-cont' + (interactive ? '-interactive' : '')} onKeyDown={e => e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%' }}>
                 {this.urlContent}
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index 7fe1436c7..f3b43501b 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -158,7 +158,7 @@
         width: 100%;
         border-radius: 100%;
         flex-direction: column;
-        margin-top: -4px;
+        // margin-top: -4px;
 
         svg {
             width: 60% !important;
@@ -427,11 +427,11 @@
     }
 
     .dropbox-background {
-        width: 100vw;
-        height: 100vh;
-        top: 0;
+        width: 200vw;
+        height: 200vh;
+        top: -100vh;
         z-index: 20;
-        left: 0;
+        left: -100vw;
         background: transparent;
         position: fixed;
     }
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 468bcc4d8..28e6eaf1c 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -618,11 +618,10 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     const selected = SelectionManager.Docs().lastElement();
     // prettier-ignore
-    const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => any; setDoc: () => void; setMode?: () => void }> = new Map([
+    const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => any; setDoc: () => void;}> = new Map([
         ['font', {
             checkResult: () => RichTextMenu.Instance?.fontFamily,
             setDoc: () => value && RichTextMenu.Instance.setFontFamily(value),
-            setMode: () => Doc.UserDoc().textAlign = value,
         }],
         ['highlight', {
             checkResult: () =>(selected ?? Doc.UserDoc())._fontHighlight,
@@ -645,8 +644,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
     if (checkResult) {
         return map.get(attr)?.checkResult();
     }
-    if (editorView?.state) map.get(attr)?.setDoc();
-    else map.get(attr)?.setMode?.();
+    map.get(attr)?.setDoc?.();
 });
 
 type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'bullet' | 'decimal';
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3ce2366f8..ee4249b02 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1470,20 +1470,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 //      but that's changed, so this shouldn't be needed.
                 //e.stopPropagation();  // if the text box is selected, then it consumes all down events
                 document.addEventListener('pointerup', this.onSelectEnd);
-                document.addEventListener('pointermove', this.onSelectMove);
             }
         }
         if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
             e.preventDefault();
         }
     };
-    onSelectMove = (e: PointerEvent) => {
-        this.props.DocumentView?.().docView?.cancelMoveEvents();
-        e.stopPropagation();
-    };
     onSelectEnd = (e: PointerEvent) => {
         document.removeEventListener('pointerup', this.onSelectEnd);
-        document.removeEventListener('pointermove', this.onSelectMove);
     };
     onPointerUp = (e: React.PointerEvent): void => {
         const editor = this._editorView!;
@@ -1534,6 +1528,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     @observable public static Focused: FormattedTextBox | undefined;
 
     onClick = (e: React.MouseEvent): void => {
+        if (!this.props.isContentActive()) return;
         if ((e.nativeEvent as any).handledByInnerReactInstance) {
             e.stopPropagation();
             return;
@@ -1876,7 +1871,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     r?.addEventListener(
                         'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
                         (e: WheelEvent) => {
-                            if (this.isContentActive()) {
+                            if (this.props.isContentActive()) {
                                 if (!NumCast(this.layoutDoc._scrollTop) && e.deltaY <= 0) e.preventDefault();
                                 e.stopPropagation();
                             }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index e79e7472a..3376c29a9 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1027,8 +1027,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) =>
-        Doc.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
+    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive(outsideReaction);
+    //.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
 
     /**
      * For sorting the array so that the order is maintained when it is dropped.
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index eb3087399..c5060a2c2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -381,7 +381,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
             const target = e.target as any;
             if (e.target && (target.className.includes('endOfContent') || (target.parentElement.className !== 'textLayer' && target.parentElement.parentElement?.className !== 'textLayer'))) {
                 this._textSelecting = false;
-                document.addEventListener('pointermove', this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called
             } else {
                 // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee.
                 setTimeout(
@@ -391,7 +390,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
                 this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' });
                 document.addEventListener('pointerup', this.onSelectEnd);
-                document.addEventListener('pointermove', this.onSelectMove);
             }
         }
     };
@@ -401,12 +399,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.isAnnotating = false;
         this._marqueeing = undefined;
         this._textSelecting = true;
-        document.removeEventListener('pointermove', this.onSelectMove);
-    };
-
-    onSelectMove = (e: PointerEvent) => {
-        this.props.DocumentView?.().docView?.cancelMoveEvents();
-        e.stopPropagation();
     };
 
     @action
@@ -414,7 +406,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.isAnnotating = false;
         clearStyleSheetRules(PDFViewer._annotationStyle);
         this.props.select(false);
-        document.removeEventListener('pointermove', this.onSelectMove);
         document.removeEventListener('pointerup', this.onSelectEnd);
 
         const sel = window.getSelection();
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 4d82551db..553c4525c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -31,7 +31,7 @@ export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
         const onDelegate = Object.keys(doc).includes(key);
         const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-        return !Field.IsField(field) ? '' : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
+        return !Field.IsField(field) ? '' : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : field instanceof ScriptField ? `$=${field.script.originalScript}` : Field.toScriptString(field));
     }
     export function toScriptString(field: Field): string {
         switch (typeof field) {
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index feb419597..2b8750714 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -114,7 +114,7 @@ export class ScriptField extends ObjectField {
     }
 
     [ToScriptString]() {
-        return 'script field';
+        return this.script.originalScript;
     }
     [ToString]() {
         return this.script.originalScript;
-- 
cgit v1.2.3-70-g09d2


From fb9ec75c46bc237bc6c8df24ee998e6de90168a1 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Wed, 12 Apr 2023 20:55:14 -0400
Subject: readonly fields and schema key info display

---
 src/client/documents/Documents.ts                  | 51 ++++++++++++----------
 .../collectionSchema/CollectionSchemaView.scss     | 11 ++++-
 .../collectionSchema/CollectionSchemaView.tsx      | 48 +++++++++++++++-----
 .../collections/collectionSchema/SchemaRowBox.tsx  |  1 +
 .../collectionSchema/SchemaTableCell.tsx           |  8 +++-
 5 files changed, 82 insertions(+), 37 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 979b294e0..6b60c1801 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -69,12 +69,14 @@ class EmptyBox {
 }
 export abstract class FInfo {
     description: string = '';
+    readOnly: boolean = false;
     fieldType?: string;
     values?: Field[];
     // format?: string; // format to display values (e.g, decimal places, $, etc)
     // parse?: ScriptField; // parse a value from a string
-    constructor(d: string) {
+    constructor(d: string, readOnly?: boolean) {
         this.description = d;
+        this.readOnly = readOnly ?? false;
     }
 }
 class BoolInfo extends FInfo {
@@ -84,16 +86,16 @@ class BoolInfo extends FInfo {
 class NumInfo extends FInfo {
     fieldType? = 'number';
     values?: number[] = [];
-    constructor(d: string, values?: number[]) {
-        super(d);
+    constructor(d: string, readOnly?: boolean, values?: number[]) {
+        super(d, readOnly);
         this.values = values;
     }
 }
 class StrInfo extends FInfo {
     fieldType? = 'string';
     values?: string[] = [];
-    constructor(d: string, values?: string[]) {
-        super(d);
+    constructor(d: string, readOnly?: boolean, values?: string[]) {
+        super(d, readOnly);
         this.values = values;
     }
 }
@@ -101,21 +103,24 @@ class DocInfo extends FInfo {
     fieldType? = 'Doc';
     values?: Doc[] = [];
     constructor(d: string, values?: Doc[]) {
-        super(d);
+        super(d, true);
         this.values = values;
     }
 }
 class DimInfo extends FInfo {
     fieldType? = 'DimUnit';
     values? = [DimUnit.Pixel, DimUnit.Ratio];
+    readOnly = true;
 }
 class PEInfo extends FInfo {
     fieldType? = 'pointerEvents';
     values? = ['all', 'none'];
+    readOnly = true;
 }
 class DAInfo extends FInfo {
     fieldType? = 'dropActionType';
     values? = ['alias', 'copy', 'move', 'same', 'proto', 'none'];
+    readOnly = true;
 }
 type BOOLt = BoolInfo | boolean;
 type NUMt = NumInfo | number;
@@ -127,12 +132,12 @@ type DROPt = DAInfo | dropActionType;
 export class DocumentOptions {
     x?: NUMt = new NumInfo('x coordinate of document in a freeform view');
     y?: NUMt = new NumInfo('y coordinage of document in a freeform view');
-    z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', [1, 0]);
-    system?: BOOLt = new BoolInfo('is this a system created/owned doc');
-    type?: STRt = new StrInfo('type of document', Array.from(Object.keys(DocumentType)));
+    z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, [1, 0]);
+    system?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
+    type?: STRt = new StrInfo('type of document', true, Array.from(Object.keys(DocumentType)));
     title?: string;
     _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
-    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?');
+    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true);
     childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
     targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
     userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
@@ -146,14 +151,14 @@ export class DocumentOptions {
     _panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
     _width?: NUMt = new NumInfo('displayed width of a document');
     _height?: NUMt = new NumInfo('displayed height of document');
-    _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)');
-    _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)');
-    _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
-    _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
-    _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
-    _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
-    _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
-    _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
+    _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)', true);
+    _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)', true);
+    _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', true);
+    _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers', true);
+    _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true);
+    _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true);
+    _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)', true);
+    _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content', true);
     _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
@@ -196,11 +201,11 @@ export class DocumentOptions {
     _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
     _timecodeToHide?: number; // the time that a document should be hidden
     _timelineLabel?: boolean; // whether the document exists on a timeline
-    '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection');
-    '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection');
-    'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view');
-    'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view');
-    'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)');
+    '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection', true);
+    '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection', true);
+    'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view', true);
+    'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view', true);
+    'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
     openFactoryAsDelegate?: boolean; //
     lat?: number;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 6118aef3f..a9434fde3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -20,6 +20,7 @@
             background: $light-gray;
             position: absolute;
             min-width: 200px;
+            max-width: 400px;
             display: flex;
             flex-direction: column;
             align-items: flex-start;
@@ -30,14 +31,20 @@
                 margin: 10px;
             }
 
-            .schema-key-search-result {
+            .schema-search-result {
                 cursor: pointer;
-                padding: 2px 10px;
+                padding: 5px 10px;
                 width: 100%;
 
                 &:hover {
                     background-color: $medium-gray;
                 }
+
+                .schema-search-result-type,
+                .schema-search-result-desc {
+                    color: $dark-gray;
+                    font-size: $body-text;
+                }
             }
 
             .schema-key-search,
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 245e1abc0..d3992d12c 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -9,7 +9,7 @@ import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
-import { Docs, DocUtils } from '../../../documents/Documents';
+import { Docs, DocumentOptions, DocUtils, FInfo } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -40,6 +40,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _closestDropIndex: number = 0;
     private _previewRef: HTMLDivElement | null = null;
     private _makeNewColumn: boolean = false;
+    private _documentOptions: DocumentOptions = new DocumentOptions();
 
     public static _rowHeight: number = 40;
     public static _minColWidth: number = 25;
@@ -54,7 +55,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _colEles: HTMLDivElement[] = [];
     @observable _displayColumnWidths: number[] | undefined;
     @observable _columnMenuIndex: number | undefined;
-    @observable _menuOptions: string[] = [];
+    @observable _menuOptions: [string, { description: string; type: string; readOnly: boolean }][] = [];
     @observable _newFieldWarning: string = '';
     @observable _makeNewField: boolean = false;
     @observable _newFieldDefault: any = 0;
@@ -63,7 +64,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _filterColumnIndex: number | undefined;
     @observable _filterSearchValue: string = '';
 
-    get documentKeys() {
+    get keyInfos() {
         const docs = this.childDocs;
         const keys: { [key: string]: boolean } = {};
         // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
@@ -75,7 +76,22 @@ export class CollectionSchemaView extends CollectionSubView() {
         untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
 
         // this.columns.forEach(key => (keys[key.heading] = true));
-        return Array.from(Object.keys(keys));
+
+        let computedKeys: { [key: string]: { description: string; type: string; readOnly: boolean } } = {};
+        Object.keys(keys).forEach((key: string) => {
+            computedKeys[key] = { description: '', type: '', readOnly: false };
+        });
+
+        Object.entries(this._documentOptions).forEach((pair: [string, any]) => {
+            const info: FInfo = pair[1];
+            computedKeys[pair[0]] = { description: info.description, type: info.fieldType ?? '', readOnly: info.readOnly };
+        });
+
+        return computedKeys;
+    }
+
+    get documentKeys() {
+        return Object.keys(this.keyInfos);
     }
 
     @computed get previewWidth() {
@@ -441,7 +457,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     onSearchKeyDown = (e: React.KeyboardEvent) => {
         switch (e.key) {
             case 'Enter':
-                this._menuOptions.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuOptions[0]) : action(() => (this._makeNewField = true))();
+                const menuKeys = Object.keys(this._menuOptions);
+                menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(menuKeys[0]) : action(() => (this._makeNewField = true))();
                 break;
             case 'Escape':
                 this.closeColumnMenu();
@@ -470,7 +487,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._makeNewColumn = false;
         this._columnMenuIndex = index;
         this._menuValue = '';
-        this._menuOptions = this.documentKeys;
+        this._menuOptions = Object.entries(this.keyInfos);
         this._makeNewField = false;
         this._newFieldWarning = '';
         this._makeNewField = false;
@@ -516,7 +533,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     updateKeySearch = (e: React.ChangeEvent<HTMLInputElement>) => {
         this._menuValue = e.target.value;
-        this._menuOptions = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
+        this._menuOptions = Object.entries(this.keyInfos).filter(value => value[0].toLowerCase().includes(this._menuValue.toLowerCase()));
     };
 
     getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
@@ -618,14 +635,23 @@ export class CollectionSchemaView extends CollectionSubView() {
                             { passive: false }
                         )
                     }>
-                    {this._menuOptions.map(key => (
+                    {this._menuOptions.map(([key, info]) => (
                         <div
-                            className="schema-key-search-result"
+                            className="schema-search-result"
                             onPointerDown={e => {
                                 e.stopPropagation();
                                 this.setKey(key);
                             }}>
-                            {key}
+                            <p>
+                                <span className="schema-search-result-key">
+                                    {key}
+                                    {info.type ? ', ' : ''}
+                                </span>
+                                <span className="schema-search-result-type" style={{ color: info.readOnly ? 'red' : 'inherit' }}>
+                                    {info.type}
+                                </span>
+                            </p>
+                            <p className="schema-search-result-desc">{info.description}</p>
                         </div>
                     ))}
                 </div>
@@ -825,7 +851,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                 {this.props.childDocs().docs.map((doc: Doc, index: number) => {
                     const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.schema.props.DataDoc;
                     return (
-                        <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
+                        <div className="schema-row-wrapper" style={{ height: CollectionSchemaView._rowHeight }}>
                             <DocumentView
                                 key={doc[Id]}
                                 {...this.props.schema.props}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 2defaae00..aa8a61205 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -123,6 +123,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <SchemaTableCell
                             key={key}
                             Document={this.rootDoc}
+                            schemaView={this.schemaView}
                             fieldKey={key}
                             columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth}
                             isRowActive={this.props.isContentActive}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 13e45963e..42c9f9d48 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -9,9 +9,11 @@ import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { DefaultStyleProvider } from '../../StyleProvider';
 import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
+import { computed } from 'mobx';
 
 export interface SchemaTableCellProps {
     Document: Doc;
+    schemaView: CollectionSchemaView | undefined;
     fieldKey: string;
     columnWidth: number;
     isRowActive: () => boolean | undefined;
@@ -20,6 +22,10 @@ export interface SchemaTableCellProps {
 
 @observer
 export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
+    get readOnly() {
+        return this.props.schemaView?.keyInfos[this.props.fieldKey].readOnly;
+    }
+
     render() {
         const props: FieldViewProps = {
             Document: this.props.Document,
@@ -50,7 +56,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
 
         return (
             <div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
-                <div className="schemacell-edit-wrapper" style={this.props.isRowActive() ? { cursor: 'text', pointerEvents: 'auto' } : { cursor: 'default', pointerEvents: 'none' }}>
+                <div className="schemacell-edit-wrapper" style={this.props.isRowActive() && !this.readOnly ? { cursor: 'text', pointerEvents: 'auto' } : { cursor: 'default', pointerEvents: 'none' }}>
                     <EditableView
                         contents={<FieldView {...props} />}
                         GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
-- 
cgit v1.2.3-70-g09d2


From 5cd64622f14ede408d3baca4a10d155b60392e46 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 13 Apr 2023 22:22:07 -0400
Subject: lots of changes to get rid of ContainingCollectionDoc and
 ContainingCollectionView props.

---
 src/client/documents/Documents.ts                  |  8 +--
 src/client/util/CurrentUserUtils.ts                |  2 +-
 src/client/util/DocumentManager.ts                 | 83 ++++++++++------------
 src/client/util/DragManager.ts                     |  4 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |  6 +-
 src/client/util/ReplayMovements.ts                 | 64 ++++++-----------
 src/client/util/SelectionManager.ts                |  6 +-
 src/client/util/SharingManager.tsx                 |  5 +-
 src/client/util/TrackMovements.ts                  | 36 +++++-----
 src/client/views/DashboardView.tsx                 | 17 ++---
 src/client/views/DocComponent.tsx                  |  1 -
 src/client/views/DocumentDecorations.tsx           |  8 +--
 src/client/views/GestureOverlay.tsx                |  4 +-
 src/client/views/InkStrokeProperties.ts            |  3 +-
 src/client/views/LightboxView.tsx                  |  4 +-
 src/client/views/MainView.tsx                      | 20 ++----
 src/client/views/OverlayView.tsx                   | 10 +--
 src/client/views/Palette.tsx                       | 31 ++++----
 src/client/views/PropertiesDocContextSelector.tsx  | 22 ++++--
 src/client/views/PropertiesView.tsx                |  2 -
 src/client/views/StyleProvider.tsx                 | 11 ++-
 src/client/views/TemplateMenu.tsx                  |  2 -
 .../views/collections/CollectionCarouselView.tsx   |  1 -
 src/client/views/collections/CollectionMenu.tsx    |  6 +-
 .../views/collections/CollectionNoteTakingView.tsx |  4 --
 .../views/collections/CollectionPileView.tsx       |  2 +-
 .../collections/CollectionStackedTimeline.tsx      |  2 -
 .../views/collections/CollectionStackingView.tsx   |  4 --
 .../views/collections/CollectionTreeView.tsx       | 10 +--
 src/client/views/collections/CollectionView.tsx    |  5 +-
 src/client/views/collections/TabDocView.tsx        |  6 +-
 src/client/views/collections/TreeView.tsx          | 54 ++++++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 31 ++++----
 .../collectionLinear/CollectionLinearView.tsx      |  5 +-
 .../CollectionMulticolumnView.tsx                  |  2 -
 .../CollectionMultirowView.tsx                     |  2 -
 .../collectionSchema/CollectionSchemaView.tsx      |  6 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |  2 +-
 .../collectionSchema/SchemaTableCell.tsx           |  2 -
 src/client/views/linking/LinkMenuItem.tsx          | 21 +-----
 src/client/views/linking/LinkPopup.tsx             |  4 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  7 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  1 -
 src/client/views/nodes/DocumentLinksButton.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 12 ++--
 src/client/views/nodes/FieldView.tsx               |  1 +
 src/client/views/nodes/KeyValuePair.tsx            |  2 -
 src/client/views/nodes/LinkDocPreview.tsx          |  4 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  5 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  4 +-
 .../views/nodes/formattedText/DashDocView.tsx      |  7 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |  5 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 20 ++++--
 src/client/views/nodes/trails/PresBox.tsx          |  1 -
 src/client/views/nodes/trails/PresElementBox.tsx   | 27 ++++---
 src/client/views/topbar/TopBar.tsx                 | 51 ++-----------
 src/mobile/AudioUpload.tsx                         | 76 ++++++++++----------
 src/mobile/MobileInterface.tsx                     |  4 +-
 58 files changed, 291 insertions(+), 456 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8d97ce5af..09b45a481 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1285,10 +1285,6 @@ export namespace DocUtils {
         return rangeFilteredDocs;
     }
 
-    export function DefaultFocus(doc: Doc, options: DocFocusOptions) {
-        return undefined;
-    }
-
     export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = [];
 
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) {
@@ -1299,10 +1295,9 @@ export namespace DocUtils {
         });
     }
 
-    export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
+    export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, showPopup?: number[]) {
         if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
         const sv = DocumentManager.Instance.getDocumentView(source);
-        if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target) return;
         if (target.doc === Doc.UserDoc()) return undefined;
 
         const makeLink = action((linkDoc: Doc, showPopup?: number[]) => {
@@ -1371,7 +1366,6 @@ export namespace DocUtils {
                         value: 'any',
                         _readOnly_: 'boolean',
                         scriptContext: 'any',
-                        thisContainer: Doc.name,
                         documentView: Doc.name,
                         heading: Doc.name,
                         checked: 'boolean',
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 76ea3e3ea..abf7313a4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -110,7 +110,7 @@ export class CurrentUserUtils {
         const tempClicks = DocCast(doc[field]);
         const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, system: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
-            { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCast(thisContainer.target).then((target) => target && (target.proto.data = new List([self])))"},
+            { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCast(documentView?.props.docViewPath().lastElement()?.rootDoc.target).then((target) => target && (target.proto.data = new List([self])))"},
             { opts: { title: "Open Detail On Right",  targetScriptKey: "onChildDoubleClick"}, script: `openDoc(self.doubleClickView.${OpenWhere.addRight})`}];
         const reqdClickList = reqdTempOpts.map(opts => {
             const allOpts = {...reqdClickOpts, ...opts.opts};
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ccf370662..ad89e8653 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,15 +1,11 @@
-import { loadAsync } from 'jszip';
-import { action, observable, ObservableSet, runInAction } from 'mobx';
+import { action, observable, ObservableSet } from 'mobx';
 import { AnimationSym, Doc, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { listSpec } from '../../fields/Schema';
 import { Cast, DocCast, StrCast } from '../../fields/Types';
 import { AudioField } from '../../fields/URLField';
-import { emptyFunction } from '../../Utils';
 import { CollectionViewType } from '../documents/DocumentTypes';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
-import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
-import { CollectionView } from '../views/collections/CollectionView';
 import { TabDocView } from '../views/collections/TabDocView';
 import { LightboxView } from '../views/LightboxView';
 import { MainView } from '../views/MainView';
@@ -39,7 +35,7 @@ export class DocumentManager {
     private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = [];
     public AddViewRenderedCb = (doc: Opt<Doc>, func: (dv: DocumentView) => any) => {
         if (doc) {
-            const dv = this.getDocumentViewById(doc[Id]);
+            const dv = this.getDocumentView(doc);
             this._viewRenderedCbs.push({ doc, func });
             if (dv) {
                 this.callAddViewFuncs(dv);
@@ -129,42 +125,24 @@ export class DocumentManager {
         return this.getDocumentViewsById(doc[Id]);
     }
 
-    public getDocumentViewById(id: string, preferredCollection?: CollectionView): DocumentView | undefined {
-        if (!id) return undefined;
-        let toReturn: DocumentView | undefined;
-        const passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
-
-        for (const pass of passes) {
-            Array.from(DocumentManager.Instance.DocumentViews).map(view => {
-                if (view.rootDoc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
-                    toReturn = view;
-                    return;
-                }
-            });
-            if (!toReturn) {
-                Array.from(DocumentManager.Instance.DocumentViews).map(view => {
-                    const doc = view.rootDoc.proto;
-                    if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
-                        toReturn = view;
-                    }
-                });
-            } else {
-                break;
-            }
-        }
-
-        return toReturn;
-    }
-
-    public getDocumentView(toFind: Doc, preferredCollection?: CollectionView): DocumentView | undefined {
-        const found =
+    public getDocumentView(toFind: Doc | undefined, preferredCollection?: DocumentView): DocumentView | undefined {
+        const doc =
+            // bcz: this was temporary code used to match documents by data url instead of by id. intended only for repairing the DB
             // Array.from(DocumentManager.Instance.DocumentViews).find(
             //     dv =>
             //         ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFind.data as any)?.url?.href) ||
             //         ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFind.annotationOn)?.data as any)?.url?.href)
             // )?.rootDoc ??
             toFind;
-        return this.getDocumentViewById(found[Id], preferredCollection);
+        const docViewArray = Array.from(DocumentManager.Instance.DocumentViews);
+        const passes = !doc ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined];
+        return passes.reduce(
+            (pass, toReturn) =>
+                toReturn ??
+                docViewArray.filter(view => view.rootDoc === doc).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection) ??
+                docViewArray.filter(view => Doc.GetProto(view.rootDoc) === doc).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection),
+            undefined as Opt<DocumentView>
+        );
     }
 
     public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
@@ -247,7 +225,8 @@ export class DocumentManager {
     public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => {
         const docViewPath = targetDocView.docViewPath.slice();
         let rootContextView = docViewPath.shift();
-        return rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined }));
+        await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined })));
+        if (options.toggleTarget && (!options.didMove || targetDocView.rootDoc.hidden)) targetDocView.rootDoc.hidden = !targetDocView.rootDoc.hidden;
     };
 
     // shows a document by first:
@@ -318,15 +297,25 @@ export class DocumentManager {
         }
     }
 }
-export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
-    const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc);
-    const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView);
-    if (dv) {
-        DocumentManager.Instance.showDocumentView(dv, { willZoomCentered: true });
-    } else {
-        const context = doc.context !== Doc.MyFilesystem && Cast(doc.context, Doc, null);
-        const showDoc = context || doc;
-        DocumentManager.Instance.showDocument(Doc.BestAlias(showDoc), { openLocation: OpenWhere.addRight }, () => DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }));
-    }
+export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCentered: true, openLocation: OpenWhere.addRight }, containingDoc?: Doc) {
+    const func = () => {
+        const cv = DocumentManager.Instance.getDocumentView(containingDoc);
+        const dv = DocumentManager.Instance.getDocumentView(doc, cv);
+        if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) {
+            DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc));
+        } else {
+            const showDoc = Doc.BestAlias(DocCast((containingDoc ?? doc.context) !== Doc.MyFilesystem ? containingDoc ?? doc.context : undefined, doc));
+            DocumentManager.Instance.showDocument(showDoc, { ...options, toggleTarget: undefined }, () => DocumentManager.Instance.showDocument(doc, options)).then(() => {
+                const cv = DocumentManager.Instance.getDocumentView(containingDoc);
+                const dv = DocumentManager.Instance.getDocumentView(doc, cv);
+                dv && Doc.linkFollowHighlight(dv.rootDoc);
+            });
+        }
+    };
+    if (doc.hidden) {
+        doc.hidden = false;
+        options.toggleTarget = false;
+        setTimeout(func);
+    } else func();
 }
 ScriptingGlobals.add(DocFocusOrOpen);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 7d2aa813f..7e6de5e67 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -3,10 +3,8 @@ import { DateField } from '../../fields/DateField';
 import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
-import { listSpec } from '../../fields/Schema';
-import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
 import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
+import { BoolCast, ScriptCast, StrCast } from '../../fields/Types';
 import { emptyFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import * as globalCssVariables from '../views/global/globalCssVariables.scss';
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 559958c2b..76b1323c4 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -14,12 +14,14 @@ import { AcceptableMedia, Upload } from '../../../server/SharedMediaTypes';
 import { Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
 import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
 import { DocumentManager } from '../DocumentManager';
 import './DirectoryImportBox.scss';
 import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry';
 import React = require('react');
+import _ = require('lodash');
 
 const unsupported = ['text/html', 'text/plain'];
 
@@ -155,8 +157,8 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
             x: NumCast(doc.x),
             y: NumCast(doc.y) + offset,
         };
-        const parent = this.props.ContainingCollectionView;
-        if (parent) {
+        const parent = _.nth(this.props.docViewPath(), -2); // last element of path is this box's document view, 2nd to last is any collection or other document that may contain it.
+        if (parent?.rootDoc.type === DocumentType.COL) {
             let importContainer: Doc;
             if (docs.length < 50) {
                 importContainer = Docs.Create.MasonryDocument(docs, options);
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index d5bffc5e2..40261985a 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -7,6 +7,7 @@ import { CollectionDockingView } from '../views/collections/CollectionDockingVie
 import { DocServer } from '../DocServer';
 import { Movement, Presentation } from './TrackMovements';
 import { OpenWhereMod } from '../views/nodes/DocumentView';
+import { returnTransparent } from '../../Utils';
 
 export class ReplayMovements {
     private timers: NodeJS.Timeout[] | null;
@@ -60,17 +61,11 @@ export class ReplayMovements {
             return;
         }
 
-        let docIdtoDoc: Map<string, Doc> = new Map();
-        try {
-            docIdtoDoc = await this.loadPresentation(presentation);
-        } catch {
-            console.error('[recordingApi.ts] setVideoBox(): error loading presentation - no replay movements');
-            throw 'error loading docs from server';
-        }
+        const docIdtoDoc = this.loadPresentation(presentation);
 
         this.videoBoxDisposeFunc = reaction(
             () => ({ playing: videoBox._playing, timeViewed: videoBox.player?.currentTime || 0 }),
-            ({ playing, timeViewed }) => (playing ? this.playMovements(presentation, docIdtoDoc, timeViewed) : this.pauseMovements())
+            ({ playing, timeViewed }) => (playing ? this.playMovements(presentation, timeViewed) : this.pauseMovements())
         );
         this.videoBox = videoBox;
     };
@@ -93,45 +88,33 @@ export class ReplayMovements {
         this.pauseMovements();
     };
 
-    loadPresentation = async (presentation: Presentation) => {
+    loadPresentation = (presentation: Presentation) => {
         const { movements } = presentation;
         if (movements === null) {
             throw '[recordingApi.ts] followMovements() failed: no presentation data';
         }
 
         // generate a set of all unique docIds
-        const docIds = new Set<string>();
-        for (const { docId } of movements) {
-            if (!docIds.has(docId)) docIds.add(docId);
-        }
-
-        const docIdtoDoc = new Map<string, Doc>();
-
-        let refFields = await DocServer.GetRefFields([...docIds.keys()]);
-        for (const docId in refFields) {
-            if (!refFields[docId]) {
-                throw `one field was undefined`;
-            }
-            docIdtoDoc.set(docId, refFields[docId] as Doc);
+        const docs = new Set<Doc>();
+        for (const { doc } of movements) {
+            if (!docs.has(doc)) docs.add(doc);
         }
-        // console.info('loadPresentation refFields', refFields, docIdtoDoc);
 
-        return docIdtoDoc;
+        return docs;
     };
 
     // returns undefined if the docView isn't open on the screen
-    getCollectionFFView = (docId: string) => {
-        const isInView = DocumentManager.Instance.getDocumentViewById(docId);
+    getCollectionFFView = (doc: Doc) => {
+        const isInView = DocumentManager.Instance.getDocumentView(doc);
         if (isInView) {
             return isInView.ComponentView as CollectionFreeFormView;
         }
     };
 
     // will open the doc in a tab then return the CollectionFFView that holds it
-    openTab = (docId: string, docIdtoDoc: Map<string, Doc>) => {
-        const doc = docIdtoDoc.get(docId);
-        if (doc == undefined) {
-            console.error(`docIdtoDoc did not contain docId ${docId}`);
+    openTab = (doc: Doc) => {
+        if (doc === undefined) {
+            console.error(`doc undefined`);
             return undefined;
         }
         // console.log('openTab', docId, doc);
@@ -149,13 +132,12 @@ export class ReplayMovements {
         document.Document._panY = panY;
     };
 
-    getFirstMovements = (movements: Movement[]): Map<string, Movement> => {
+    getFirstMovements = (movements: Movement[]): Map<Doc, Movement> => {
         if (movements === null) return new Map();
         // generate a set of all unique docIds
-        const docIdtoFirstMove = new Map();
+        const docIdtoFirstMove = new Map<Doc, Movement>();
         for (const move of movements) {
-            const { docId } = move;
-            if (!docIdtoFirstMove.has(docId)) docIdtoFirstMove.set(docId, move);
+            if (!docIdtoFirstMove.has(move.doc)) docIdtoFirstMove.set(move.doc, move);
         }
         return docIdtoFirstMove;
     };
@@ -165,7 +147,7 @@ export class ReplayMovements {
         Doc.UserDoc().presentationMode = 'none';
     };
 
-    public playMovements = (presentation: Presentation, docIdtoDoc: Map<string, Doc>, timeViewed: number = 0) => {
+    public playMovements = (presentation: Presentation, timeViewed: number = 0) => {
         // console.info('playMovements', presentation, timeViewed, docIdtoDoc);
 
         if (presentation.movements === null || presentation.movements.length === 0) {
@@ -183,13 +165,13 @@ export class ReplayMovements {
         const handleFirstMovements = () => {
             // if the first movement is a closed tab, open it
             const firstMovement = filteredMovements[0];
-            const isClosed = this.getCollectionFFView(firstMovement.docId) === undefined;
-            if (isClosed) this.openTab(firstMovement.docId, docIdtoDoc);
+            const isClosed = this.getCollectionFFView(firstMovement.doc) === undefined;
+            if (isClosed) this.openTab(firstMovement.doc);
 
             // for the open tabs, set it to the first move
             const docIdtoFirstMove = this.getFirstMovements(filteredMovements);
-            for (const [docId, firstMove] of docIdtoFirstMove) {
-                const colFFView = this.getCollectionFFView(docId);
+            for (const [doc, firstMove] of docIdtoFirstMove) {
+                const colFFView = this.getCollectionFFView(doc);
                 if (colFFView) this.zoomAndPan(firstMove, colFFView);
             }
         };
@@ -200,12 +182,12 @@ export class ReplayMovements {
             const timeDiff = movement.time - timeViewed * 1000;
 
             return setTimeout(() => {
-                const collectionFFView = this.getCollectionFFView(movement.docId);
+                const collectionFFView = this.getCollectionFFView(movement.doc);
                 if (collectionFFView) {
                     this.zoomAndPan(movement, collectionFFView);
                 } else {
                     // tab wasn't open - open it and play the movement
-                    const openedColFFView = this.openTab(movement.docId, docIdtoDoc);
+                    const openedColFFView = this.openTab(movement.doc);
                     console.log('openedColFFView', openedColFFView);
                     openedColFFView && this.zoomAndPan(movement, openedColFFView);
                 }
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 313c255a0..bfad93334 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,4 +1,3 @@
-import { ModalManager } from '@material-ui/core';
 import { action, observable, ObservableMap } from 'mobx';
 import { computedFn } from 'mobx-utils';
 import { Doc, Opt } from '../../fields/Doc';
@@ -64,8 +63,9 @@ export namespace SelectionManager {
     export function DeselectView(docView?: DocumentView): void {
         manager.DeselectView(docView);
     }
-    export function SelectView(docView: DocumentView, ctrlPressed: boolean): void {
-        manager.SelectView(docView, ctrlPressed);
+    export function SelectView(docView: DocumentView | undefined, ctrlPressed: boolean): void {
+        if (!docView) DeselectAll();
+        else manager.SelectView(docView, ctrlPressed);
     }
     export function SelectSchemaViewDoc(document: Opt<Doc>, deselectAllFirst?: boolean): void {
         if (deselectAllFirst) manager.DeselectAll();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3c05af4bb..a73eda04c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -370,11 +370,10 @@ export class SharingManager extends React.Component<{}> {
         const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
         return (
             <span
-                className={'focus-span'}
+                className="focus-span"
                 title={title}
                 onClick={() => {
-                    let context: Opt<CollectionView>;
-                    if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) {
+                    if (this.targetDoc && this.targetDocView && docs.length === 1) {
                         DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true });
                     }
                 }}
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index 4a2ccd706..2f16307b3 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -9,7 +9,7 @@ export type Movement = {
     panX: number;
     panY: number;
     scale: number;
-    docId: string;
+    doc: Doc;
 };
 
 export type Presentation = {
@@ -28,7 +28,7 @@ export class TrackMovements {
     private tracking: boolean;
     private absoluteStart: number;
     // instance variable for holding the FFViews and their disposers
-    private recordingFFViews: Map<string, IReactionDisposer> | null;
+    private recordingFFViews: Map<Doc, IReactionDisposer> | null;
     private tabChangeDisposeFunc: IReactionDisposer | null;
 
     // create static instance and getter for global use
@@ -55,33 +55,33 @@ export class TrackMovements {
         return this.currentPresentation.movements === null;
     }
 
-    private addRecordingFFView(doc: Doc, key: string = doc[Id]): void {
+    private addRecordingFFView(doc: Doc): void {
         // console.info('adding dispose func : docId', key, 'doc', doc);
 
         if (this.recordingFFViews === null) {
             console.warn('addFFView on null RecordingApi');
             return;
         }
-        if (this.recordingFFViews.has(key)) {
-            console.warn('addFFView : key already in map');
+        if (this.recordingFFViews.has(doc)) {
+            console.warn('addFFView : doc already in map');
             return;
         }
 
         const disposeFunc = reaction(
             () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0) }),
-            res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, key, res.scale)
+            res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, doc, res.scale)
         );
-        this.recordingFFViews?.set(key, disposeFunc);
+        this.recordingFFViews?.set(doc, disposeFunc);
     }
 
-    private removeRecordingFFView = (key: string) => {
+    private removeRecordingFFView = (doc: Doc) => {
         // console.info('removing dispose func : docId', key);
         if (this.recordingFFViews === null) {
             console.warn('removeFFView on null RecordingApi');
             return;
         }
-        this.recordingFFViews.get(key)?.();
-        this.recordingFFViews.delete(key);
+        this.recordingFFViews.get(doc)?.();
+        this.recordingFFViews.delete(doc);
     };
 
     // in the case where only one tab was changed (updates not across dashboards), set only one to true
@@ -90,15 +90,15 @@ export class TrackMovements {
 
         // so that the size comparisons are correct, we must filter to only the FFViews
         const isFFView = (doc: Doc) => doc && 'viewType' in doc && doc.viewType === 'freeform';
-        const tabbedFFViews = new Set<string>();
+        const tabbedFFViews = new Set<Doc>();
         for (const DashDoc of tabbedDocs) {
-            if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc[Id]);
+            if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc);
         }
 
         // new tab was added - need to add it
         if (tabbedFFViews.size > this.recordingFFViews.size) {
             for (const DashDoc of tabbedDocs) {
-                if (!this.recordingFFViews.has(DashDoc[Id])) {
+                if (!this.recordingFFViews.has(DashDoc)) {
                     if (isFFView(DashDoc)) {
                         this.addRecordingFFView(DashDoc);
 
@@ -110,9 +110,9 @@ export class TrackMovements {
         }
         // tab was removed - need to remove it from recordingFFViews
         else if (tabbedFFViews.size < this.recordingFFViews.size) {
-            for (const [key] of this.recordingFFViews) {
-                if (!tabbedFFViews.has(key)) {
-                    this.removeRecordingFFView(key);
+            for (const [doc] of this.recordingFFViews) {
+                if (!tabbedFFViews.has(doc)) {
+                    this.removeRecordingFFView(doc);
                     if (onlyOne) return;
                 }
             }
@@ -214,7 +214,7 @@ export class TrackMovements {
         }
     };
 
-    private trackMovement = (panX: number, panY: number, docId: string, scale: number = 0) => {
+    private trackMovement = (panX: number, panY: number, doc: Doc, scale: number = 0) => {
         // ensure we are recording to track
         if (!this.tracking) {
             console.error('[recordingApi.ts] trackMovements(): tracking is false');
@@ -231,7 +231,7 @@ export class TrackMovements {
         // get the time
         const time = new Date().getTime() - this.absoluteStart;
         // make new movement object
-        const movement: Movement = { time, panX, panY, scale, docId };
+        const movement: Movement = { time, panX, panY, scale, doc };
 
         // add that movement to the current presentation data's movement array
         this.currentPresentation.movements && this.currentPresentation.movements.push(movement);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 2b586b0e2..dee161931 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -109,17 +109,8 @@ export class DashboardView extends React.Component {
                     />
                 </div>
                 <div className="button-bar">
-                    <Button text="Cancel" borderRadius={10} hoverStyle={'gray'} fontSize={FontSize.SECONDARY} onClick={this.abortCreateNewDashboard} />
-                    <Button
-                        text="Create"
-                        borderRadius={10}
-                        backgroundColor={Colors.LIGHT_BLUE}
-                        hoverStyle={'darken'}
-                        fontSize={FontSize.SECONDARY}
-                        onClick={() => {
-                            this.createNewDashboard(this.newDashboardName!, this.newDashboardColor);
-                        }}
-                    />
+                    <Button text="Cancel" onClick={this.abortCreateNewDashboard} />
+                    <Button text="Create" onClick={() => this.createNewDashboard(this.newDashboardName!, this.newDashboardColor)} />
                 </div>
             </div>
         );
@@ -163,7 +154,7 @@ export class DashboardView extends React.Component {
                 <div className="dashboard-view">
                     <div className="left-menu">
                         <div className="new-dashboard-button">
-                            <Button icon={<FaPlus />} hoverStyle="darken" backgroundColor={Colors.LIGHT_BLUE} size={Size.MEDIUM} fontSize={FontSize.HEADER} text="New" onClick={() => this.setNewDashboardName('')} borderRadius={50} />
+                            <Button icon={<FaPlus />} size={Size.MEDIUM} text="New" onClick={() => this.setNewDashboardName('')} />
                         </div>
                         <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>
                             My Dashboards
@@ -196,7 +187,7 @@ export class DashboardView extends React.Component {
                                                 e.stopPropagation();
                                                 this.onContextMenu(dashboard, e);
                                             }}>
-                                            <IconButton isCircle={true} size={Size.SMALL} hoverStyle="gray" icon={<FontAwesomeIcon color="black" size="lg" icon="bars" />} />
+                                            <Button size={Size.SMALL} icon={<FontAwesomeIcon color="black" size="lg" icon="bars" />} />
                                         </div>
                                     </div>
                                 </div>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 9fc1487a0..a59189fd2 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -52,7 +52,6 @@ export function DocComponent<P extends DocComponentProps>() {
 interface ViewBoxBaseProps {
     Document: Doc;
     DataDoc?: Doc;
-    ContainingCollectionDoc: Opt<Doc>;
     DocumentView?: () => DocumentView;
     fieldKey: string;
     isSelected: (outsideReaction?: boolean) => boolean;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9ffbe083f..9a4aaf00e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -33,6 +33,7 @@ import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
 import { RichTextField } from '../../fields/RichTextField';
 import { LinkFollower } from '../util/LinkFollower';
+import _ = require('lodash');
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -303,8 +304,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         SelectionManager.DeselectAll();
     };
 
-    onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
-
+    onSelectorClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false);
     /**
      * Handles setting up events when user clicks on the border radius editor
      * @param e PointerEvent
@@ -744,7 +744,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.props.hideDeleteButton ||
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
-                const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
+                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
                 return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
 
@@ -865,7 +865,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
                                     <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
 
-                                    {seldocview.props.renderDepth <= 1 || !seldocview.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
+                                    {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
                                 </>
                             )}
                             {useRounding && (
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 0feccb742..13faae783 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1010,14 +1010,12 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 renderDepth={0}
                 styleProvider={returnEmptyString}
                 docViewPath={returnEmptyDoclist}
-                focus={DocUtils.DefaultFocus}
+                focus={emptyFunction}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
                 docRangeFilters={returnEmptyFilter}
                 docFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
             />
         );
     };
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index e6df0801c..156825f41 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -373,10 +373,11 @@ export class InkStrokeProperties {
 
     snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => {
         const containingCollection = inkView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+        const containingDocView = inkView.props.CollectionFreeFormDocumentView?.().props.DocumentView?.();
         containingCollection?.childDocs
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
-                const testInkView = DocumentManager.Instance.getDocumentView(doc, containingCollection?.props.CollectionView);
+                const testInkView = DocumentManager.Instance.getDocumentView(doc, containingDocView);
                 const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.rootDoc ? this.excludeSelfSnapSegs(ink, controlIndex) : []);
                 if (snapped && snapped.distance < snapData.distance) {
                     const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 976c8763e..69eec8456 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -266,8 +266,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                             docFilters={this.docFilters}
                             docRangeFilters={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}
                             addDocument={undefined}
                             removeDocument={undefined}
                             whenChildContentsActiveChanged={emptyFunction}
@@ -275,7 +273,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                             pinToPres={TabDocView.PinDoc}
                             bringToFront={emptyFunction}
                             onBrowseClick={MainView.Instance.exploreMode}
-                            focus={DocUtils.DefaultFocus}
+                            focus={emptyFunction}
                         />
                     </GestureOverlay>
                 </div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 60459cf30..0384d925e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -595,14 +595,12 @@ export class MainView extends React.Component {
                     PanelWidth={this.headerBarDocWidth}
                     PanelHeight={this.headerBarDocHeight}
                     renderDepth={0}
-                    focus={DocUtils.DefaultFocus}
+                    focus={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                 />
             </div>
         );
@@ -626,14 +624,12 @@ export class MainView extends React.Component {
                     ScreenToLocalTransform={this._hideUI ? this.mainScreenToLocalXf : Transform.Identity}
                     PanelWidth={this.mainDocViewWidth}
                     PanelHeight={this.mainDocViewHeight}
-                    focus={DocUtils.DefaultFocus}
+                    focus={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                     suppressSetHeight={true}
                     renderDepth={this._hideUI ? 0 : -1}
                 />
@@ -727,14 +723,12 @@ export class MainView extends React.Component {
                         renderDepth={0}
                         isContentActive={returnTrue}
                         scriptContext={CollectionDockingView.Instance?.props.Document}
-                        focus={DocUtils.DefaultFocus}
+                        focus={emptyFunction}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
                         docFilters={returnEmptyFilter}
                         docRangeFilters={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
-                        ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
                     />
                 </div>
                 {this.docButtons}
@@ -758,7 +752,7 @@ export class MainView extends React.Component {
                     PanelHeight={this.leftMenuHeight}
                     renderDepth={0}
                     docViewPath={returnEmptyDoclist}
-                    focus={DocUtils.DefaultFocus}
+                    focus={emptyFunction}
                     styleProvider={DefaultStyleProvider}
                     isContentActive={returnTrue}
                     whenChildContentsActiveChanged={emptyFunction}
@@ -766,8 +760,6 @@ export class MainView extends React.Component {
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                     scriptContext={this}
                 />
             </div>
@@ -900,13 +892,11 @@ export class MainView extends React.Component {
                     PanelWidth={this.leftMenuFlyoutWidth}
                     PanelHeight={this.leftMenuFlyoutHeight}
                     renderDepth={0}
-                    focus={DocUtils.DefaultFocus}
+                    focus={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                 />
                 {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>}
             </div>
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index ec22128d4..bdc48d03a 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, trace } from 'mobx';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
@@ -10,15 +10,13 @@ import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, retu
 import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DragManager } from '../util/DragManager';
-import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { Transform } from '../util/Transform';
 import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView';
 import { LightboxView } from './LightboxView';
 import { MainView } from './MainView';
 import { DocumentView } from './nodes/DocumentView';
 import './OverlayView.scss';
-import { ScriptingRepl } from './ScriptingRepl';
-import { DefaultStyleProvider, testDocProps } from './StyleProvider';
+import { DefaultStyleProvider } from './StyleProvider';
 
 export type OverlayDisposer = () => void;
 
@@ -223,7 +221,7 @@ export class OverlayView extends React.Component {
                             isDocumentActive={returnTrue}
                             isContentActive={returnTrue}
                             whenChildContentsActiveChanged={emptyFunction}
-                            focus={DocUtils.DefaultFocus}
+                            focus={emptyFunction}
                             styleProvider={DefaultStyleProvider}
                             docViewPath={returnEmptyDoclist}
                             addDocTab={d.type === DocumentType.PRES ? MainView.addDocTabFunc : returnFalse}
@@ -231,8 +229,6 @@ export class OverlayView extends React.Component {
                             docFilters={returnEmptyFilter}
                             docRangeFilters={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}
                         />
                     </div>
                 );
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 954529bc9..3ad28c418 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -1,12 +1,12 @@
-import { IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc } from "../../fields/Doc";
-import { NumCast } from "../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from "../../Utils";
-import { Transform } from "../util/Transform";
-import { DocumentView } from "./nodes/DocumentView";
-import "./Palette.scss";
+import { IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../fields/Doc';
+import { NumCast } from '../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from '../../Utils';
+import { Transform } from '../util/Transform';
+import { DocumentView } from './nodes/DocumentView';
+import './Palette.scss';
 
 export interface PaletteProps {
     x: number;
@@ -23,20 +23,20 @@ export default class Palette extends React.Component<PaletteProps> {
     componentDidMount = () => {
         this._selectedDisposer = reaction(
             () => NumCast(this.props.thumbDoc.selectedIndex),
-            (i) => this._selectedIndex = i,
+            i => (this._selectedIndex = i),
             { fireImmediately: true }
         );
-    }
+    };
 
     componentWillUnmount = () => {
         this._selectedDisposer?.();
-    }
+    };
 
     render() {
         return (
             <div className="palette-container" style={{ transform: `translate(${this.props.x}px, ${this.props.y}px)` }}>
                 <div className="palette-thumb" style={{ transform: `translate(${this.props.thumb[0] - this.props.x}px, ${this.props.thumb[1] - this.props.y}px)` }}>
-                    <div className="palette-thumbContent" style={{ transform: `translate(-${(this._selectedIndex * 50) + 10}px, 0px)` }}>
+                    <div className="palette-thumbContent" style={{ transform: `translate(-${this._selectedIndex * 50 + 10}px, 0px)` }}>
                         <DocumentView
                             Document={this.props.thumbDoc}
                             DataDoc={undefined}
@@ -59,12 +59,11 @@ export default class Palette extends React.Component<PaletteProps> {
                             docFilters={returnEmptyFilter}
                             docRangeFilters={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined} />
+                        />
                         <div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
                     </div>
                 </div>
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 2c7da5931..93eec61f3 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -22,9 +22,9 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
     @computed get _docs() {
         if (!this.props.DocView) return [];
         const target = this.props.DocView.props.Document;
-        const targetContext = this.props.DocView.props.ContainingCollectionDoc;
+        const targetContext = this.props.DocView.props.docViewPath().lastElement()?.rootDoc;
         const aliases = DocListCast(target.aliases);
-        const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc && Cast(alias.context, Doc, null) !== targetContext).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
+        const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases));
         const containers = containerSets.reduce((p, set) => {
             set.map(s => p.add(s));
@@ -42,6 +42,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
         return doclayouts
             .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document))
             .filter(doc => !Doc.IsSystem(doc))
+            .filter(doc => doc !== targetContext)
             .map(doc => ({ col: doc, target }));
     }
 
@@ -53,8 +54,21 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
             col._panY = NumCast(target.y) + NumCast(target._height) / 2;
         }
         col.hidden = false;
-        this.props.addDocTab(col, (OpenWhere.toggle + ':' + OpenWhereMod.right) as OpenWhere);
-        setTimeout(() => DocFocusOrOpen(Doc.GetProto(this.props.DocView!.props.Document), col), 100);
+        //this.props.addDocTab(col, (OpenWhere.toggle + ':' + OpenWhereMod.right) as OpenWhere);
+        setTimeout(
+            () =>
+                this.props.DocView &&
+                DocFocusOrOpen(
+                    Doc.GetProto(this.props.DocView.props.Document),
+                    {
+                        //
+                        willZoomCentered: true,
+                        openLocation: (OpenWhere.toggle + ':' + OpenWhereMod.right) as OpenWhere,
+                    },
+                    col
+                ),
+            100
+        );
     };
 
     render() {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index fbc7d7696..6582c3f2a 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -341,8 +341,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         docFilters={returnEmptyFilter}
                         docRangeFilters={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
-                        ContainingCollectionDoc={undefined}
-                        ContainingCollectionView={undefined}
                         addDocument={returnFalse}
                         moveDocument={undefined}
                         removeDocument={returnFalse}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index b950b4860..739d6d819 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -18,7 +18,7 @@ import { TreeSort } from './collections/TreeView';
 import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { MainView } from './MainView';
-import { DocumentViewProps } from './nodes/DocumentView';
+import { DocumentViewProps, OpenWhere } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { SliderBox } from './nodes/SliderBox';
@@ -295,7 +295,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 default:
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
-                        : props?.ContainingCollectionDoc?._useClusters
+                        : props?.docViewPath().lastElement()?.rootDoc._useClusters
                         ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1
                         ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
@@ -316,7 +316,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
         case StyleProp.Decorations:
             const lock = () => {
-                if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform) {
+                if (props?.docViewPath().lastElement()?.rootDoc?._viewType === CollectionViewType.Freeform) {
                     return doc?.pointerEvents !== 'none' ? null : (
                         <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                             <FontAwesomeIcon icon={'lock'} style={{ color: 'red' }} size="lg" />
@@ -383,10 +383,7 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
         return doc._viewType === CollectionViewType.Docking ? null : (
             <>
                 {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => {
-                    doc.hidden = doc.hidden ? undefined : true;
-                    if (!doc.hidden) {
-                        DocFocusOrOpen(doc, props?.ContainingCollectionDoc);
-                    }
+                    DocFocusOrOpen(doc, { toggleTarget: true, willPan: true, openLocation: OpenWhere.addRight }, props?.docViewPath().lastElement()?.rootDoc);
                 })}
             </>
         );
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 45db240a9..c5a501aa6 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -114,8 +114,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 <CollectionTreeView
                     Document={Doc.MyTemplates}
                     CollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                    ContainingCollectionView={undefined}
                     styleProvider={DefaultStyleProvider}
                     setHeight={returnFalse}
                     docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index c0220f804..0e4556eb4 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -62,7 +62,6 @@ export class CollectionCarouselView extends CollectionSubView() {
                         onClick={this.onContentClick}
                         hideCaptions={showCaptions ? true : false}
                         renderDepth={this.props.renderDepth + 1}
-                        ContainingCollectionView={this.props.CollectionView}
                         LayoutTemplate={this.props.childLayoutTemplate}
                         LayoutTemplateString={this.props.childLayoutString}
                         Document={curDoc.layout}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 225743748..2007f73aa 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -134,8 +134,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                 />
             </div>
         );
@@ -642,8 +640,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                         key="float"
                         style={{
                             backgroundColor: this.props.docView.layoutDoc.z ? '121212' : undefined,
-                            pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? 'none' : undefined,
-                            color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? 'dimgrey' : undefined,
+                            pointerEvents: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._viewType !== CollectionViewType.Freeform ? 'none' : undefined,
+                            color: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._viewType !== CollectionViewType.Freeform ? 'dimgrey' : undefined,
                         }}
                         onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
                         <FontAwesomeIcon icon={['fab', 'buffer']} size={'lg'} />
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index aec0734b4..80e81bc1c 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -255,8 +255,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 hideTitle={this.props.childHideTitle?.()}
                 docRangeFilters={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
-                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                ContainingCollectionView={this.props.CollectionView}
                 addDocument={this.props.addDocument}
                 moveDocument={this.props.moveDocument}
                 removeDocument={this.props.removeDocument}
@@ -632,8 +630,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                         docFilters={this.props.docFilters}
                         docRangeFilters={this.props.docRangeFilters}
                         searchFilterDocs={this.props.searchFilterDocs}
-                        ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
                     />
                 </div>
             );
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index ba90ed8cd..fd9b0c0ce 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -30,7 +30,7 @@ export class CollectionPileView extends CollectionSubView() {
         // pileups are designed to go away when they are empty.
         this._disposers.selected = reaction(
             () => this.childDocs.length,
-            num => !num && this.props.ContainingCollectionView?.removeDocument(this.props.Document)
+            num => !num && this.props.CollectionFreeFormDocumentView?.().props.removeDocument?.(this.props.Document)
         );
     }
     componentWillUnmount() {
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index bbd81d06d..22a575989 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -815,8 +815,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={focusFunc}
                     isContentActive={returnFalse}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                     searchFilterDocs={returnEmptyDoclist}
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a85ee0e02..67f5dc9f4 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -346,8 +346,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 hideTitle={this.props.childHideTitle?.()}
                 docRangeFilters={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
-                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                ContainingCollectionView={this.props.CollectionView}
                 xPadding={NumCast(this.layoutDoc._childXPadding, this.props.childXPadding)}
                 yPadding={NumCast(this.layoutDoc._childYPadding, this.props.childYPadding)}
                 addDocument={this.props.addDocument}
@@ -678,8 +676,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         docFilters={this.props.docFilters}
                         docRangeFilters={this.props.docRangeFilters}
                         searchFilterDocs={this.props.searchFilterDocs}
-                        ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
                     />
                 </div>
             );
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4a11e8f0b..92932fb61 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -164,7 +164,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             this.props.removeDocument?.(doc);
             if (ind > 0) {
                 FormattedTextBox.SelectOnLoad = prev[Id];
-                DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView)?.select(false);
+                DocumentManager.Instance.getDocumentView(prev, this.props.DocumentView?.())?.select(false);
             }
             return true;
         }
@@ -242,8 +242,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 docFilters={returnEmptyFilter}
                 docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionDoc={this.doc}
-                ContainingCollectionView={this.props.CollectionView}
                 addDocument={returnFalse}
                 moveDocument={returnFalse}
                 removeDocument={returnFalse}
@@ -272,7 +270,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             this,
             this.doc,
             this.props.DataDoc,
-            this.props.ContainingCollectionDoc,
+            undefined,
             undefined,
             addDoc,
             this.remove,
@@ -331,10 +329,10 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     isDocumentActive={returnTrue}
                     addDocument={this.props.addDocument}
                     moveDocument={this.props.moveDocument}
+                    removeDocument={this.props.removeDocument}
                     addDocTab={this.props.addDocTab}
                     pinToPres={emptyFunction}
                     rootSelected={this.props.isSelected}
-                    removeDocument={this.props.removeDocument}
                     ScreenToLocalTransform={Transform.Identity}
                     PanelWidth={this.return35}
                     PanelHeight={this.return35}
@@ -347,8 +345,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     docFilters={this.props.docFilters}
                     docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                 />
             </div>
         );
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bc25ad43a..53fbcc3cc 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -279,7 +279,10 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             CollectionView: this,
         };
         return (
-            <div className={'collectionView'} onContextMenu={this.onContextMenu} style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? 'none' : undefined }}>
+            <div
+                className="collectionView"
+                onContextMenu={this.onContextMenu}
+                style={{ pointerEvents: this.props.DocumentView?.()?.props.docViewPath().lastElement()?.rootDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? 'none' : undefined }}>
                 {this.showIsTagged()}
                 {this.renderSubView(this.collectionViewType, props)}
             </div>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 458712999..ef8f395c5 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -425,8 +425,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     hideTitle={this.props.keyValue}
                     Document={this._document}
                     DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                     onBrowseClick={MainView.Instance.exploreMode}
                     waitForDoubleClickToClick={MainView.Instance.waitForDoubleClick}
                     isContentActive={returnTrue}
@@ -580,8 +578,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                 <CollectionFreeFormView
                     Document={this.props.document}
                     CollectionView={undefined}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                     docViewPath={returnEmptyDoclist}
                     childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
                     noOverlay={true} // don't render overlay Docs since they won't scale
@@ -603,7 +599,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     ScreenToLocalTransform={Transform.Identity}
                     renderDepth={0}
                     whenChildContentsActiveChanged={emptyFunction}
-                    focus={DocUtils.DefaultFocus}
+                    focus={emptyFunction}
                     styleProvider={TabMinimapView.miniStyleProvider}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 75e76019e..8b1dfc767 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -42,7 +42,7 @@ export interface TreeViewProps {
     prevSibling?: Doc;
     document: Doc;
     dataDoc?: Doc;
-    containerCollection: Doc;
+    treeViewParent: Doc;
     renderDepth: number;
     dropAction: dropActionType;
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
@@ -143,7 +143,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView;
     }
     @computed get MAX_EMBED_HEIGHT() {
-        return NumCast(this.props.containerCollection.maxEmbedHeight, 200);
+        return NumCast(this.props.treeViewParent.maxEmbedHeight, 200);
     }
     @computed get dataDoc() {
         return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym];
@@ -194,7 +194,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const ind = this.dataDoc[key].indexOf(doc);
 
         const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
-        res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false);
+        res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.DocumentView?.())?.select(false);
         return res;
     };
 
@@ -385,7 +385,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         };
         const addDoc = inside ? localAdd : parentAddDoc;
         const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
-        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes('add')) || forceAdd;
+        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.freezeChildren).includes('add')) || forceAdd;
         if (canAdd) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
             const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false));
@@ -406,7 +406,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     getTransform = () => this.refTransform(this._tref.current);
     embeddedPanelWidth = () => this.props.panelWidth() / (this.props.treeView.props.NativeDimScaling?.() || 1);
     embeddedPanelHeight = () => {
-        const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
         return Math.min(
             layoutDoc[HeightSym](),
             this.MAX_EMBED_HEIGHT,
@@ -416,7 +416,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 return layoutDoc._fitWidth
                     ? !Doc.NativeHeight(layoutDoc)
                         ? NumCast(layoutDoc._height)
-                        : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)))
+                        : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.treeViewParent._height)))
                     : (this.embeddedPanelWidth() * layoutDoc[HeightSym]()) / layoutDoc[WidthSym]();
             })()
         );
@@ -452,7 +452,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     this,
                     doc,
                     undefined,
-                    this.props.containerCollection,
+                    this.props.treeViewParent,
                     this.props.prevSibling,
                     addDoc,
                     remDoc,
@@ -596,7 +596,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                   this,
                                   this.layoutDoc,
                                   this.dataDoc,
-                                  this.props.containerCollection,
+                                  this.props.treeViewParent,
                                   this.props.prevSibling,
                                   addDoc,
                                   remDoc,
@@ -656,7 +656,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             this.onCheckedClick?.script.run(
                 {
                     this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
-                    heading: this.props.containerCollection.title,
+                    heading: this.props.treeViewParent.title,
                     checked: this.doc.treeViewChecked === 'check' ? 'x' : this.doc.treeViewChecked === 'x' ? 'remove' : 'check',
                     containingTreeView: this.props.treeView.props.Document,
                 },
@@ -724,9 +724,11 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
 
     @observable headerEleWidth = 0;
-    @computed get headerElements() {
+    @computed get titleButtons() {
+        const customHeaderButtons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations);
         return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
             <>
+                {customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */}
                 {this.doc.hideContextMenu ? null : (
                     <FontAwesomeIcon
                         title="context menu"
@@ -779,9 +781,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     };
 
-    onChildClick = () => {
-        return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
-    };
+    onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
 
     onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
 
@@ -906,7 +906,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 styleProvider={this.titleStyleProvider}
                 enableDragWhenActive={true}
                 onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
-                docViewPath={returnEmptyDoclist}
+                docViewPath={this.props.treeView.props.docViewPath}
                 treeViewDoc={this.props.treeView.props.Document}
                 addDocument={undefined}
                 addDocTab={this.props.addDocTab}
@@ -937,12 +937,8 @@ export class TreeView extends React.Component<TreeViewProps> {
                 docFilters={returnEmptyFilter}
                 docRangeFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={this.props.treeView.props.CollectionView}
-                ContainingCollectionDoc={this.props.treeView.props.Document}
             />
         );
-
-        const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ':afterHeader' : ''));
         return (
             <>
                 <div
@@ -958,8 +954,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     {view}
                 </div>
                 <div className="treeView-rightButtons" ref={action((r: any) => r && (this.headerEleWidth = r.getBoundingClientRect().width))}>
-                    {buttons} {/* hide and lock buttons */}
-                    {this.headerElements}
+                    {this.titleButtons}
                 </div>
             </>
         );
@@ -1017,8 +1012,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionDoc={this.props.containerCollection}
-                    ContainingCollectionView={undefined}
                     addDocument={this.props.addDocument}
                     moveDocument={this.move}
                     removeDocument={this.props.removeDoc}
@@ -1131,7 +1124,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         childDocs: Doc[],
         treeView: CollectionTreeView,
         parentTreeView: CollectionTreeView | TreeView | undefined,
-        containerCollection: Doc,
+        treeViewParent: Doc,
         dataDoc: Doc | undefined,
         parentCollectionDoc: Doc | undefined,
         containerPrevSibling: Doc | undefined,
@@ -1162,19 +1155,19 @@ export class TreeView extends React.Component<TreeViewProps> {
         hierarchyIndex?: number[],
         renderCount?: number
     ) {
-        const viewSpecScript = Cast(containerCollection.viewSpecScript, ScriptField);
+        const viewSpecScript = Cast(treeViewParent.viewSpecScript, ScriptField);
         if (viewSpecScript) {
             childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
         }
 
-        const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion, TreeSort.None));
+        const docs = TreeView.sortDocs(childDocs, StrCast(treeViewParent.treeViewSortCriterion, TreeSort.None));
         const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView.props.NativeDimScaling?.() || 1);
         const treeViewRefs = new Map<Doc, TreeView | undefined>();
         return docs
             .filter(child => child instanceof Doc)
             .map((child, i) => {
                 if (renderCount && i > renderCount) return null;
-                const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child);
+                const pair = Doc.GetLayoutDataDocPair(treeViewParent, dataDoc, child);
                 if (!pair.layout || pair.data instanceof Promise) {
                     return null;
                 }
@@ -1192,10 +1185,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     }
                 };
                 const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
-                const outdent =
-                    parentCollectionDoc?._viewType !== CollectionViewType.Tree
-                        ? undefined
-                        : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
+                const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
                 const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
                 const childLayout = Doc.Layout(pair.layout);
                 const rowHeight = () => {
@@ -1208,7 +1198,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         ref={r => treeViewRefs.set(child, r ? r : undefined)}
                         document={pair.layout}
                         dataDoc={pair.data}
-                        containerCollection={containerCollection}
+                        treeViewParent={treeViewParent}
                         prevSibling={docs[i]}
                         // TODO: [AL] add these
                         hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
@@ -1220,7 +1210,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         onCheckedClick={onCheckedClick}
                         onChildClick={onChildClick}
                         renderDepth={renderDepth}
-                        removeDoc={StrCast(containerCollection.freezeChildren).includes('remove') ? undefined : remove}
+                        removeDoc={StrCast(treeViewParent.freezeChildren).includes('remove') ? undefined : remove}
                         addDocument={addDocument}
                         styleProvider={styleProvider}
                         panelWidth={rowWidth}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7ae7be3c8..a3f5e73fb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -262,7 +262,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     selectDocuments = (docs: Doc[]) => {
         SelectionManager.DeselectAll();
-        docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true));
+        docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())).map(dv => dv && SelectionManager.SelectView(dv, true));
     };
     addDocument = (newBox: Doc | Doc[]) => {
         let retVal = false;
@@ -319,7 +319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
         if (didMove) {
             const focusTime = options?.instant ? 0 : options.zoomTime ?? 500;
-            options.zoomScale && scale && (this.Document[this.scaleFieldKey] = scale);
+            (options.zoomScale || options.willZoomCentered) && scale && (this.Document[this.scaleFieldKey] = scale);
             this.setPan(panX, panY, focusTime, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
             return focusTime;
         }
@@ -378,19 +378,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 1
             );
             if (sorted.length && refDoc && NumCast(sorted[0].layout.y) < NumCast(refDoc.y)) {
-                const topIndexed = NumCast(refDoc.y) < NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height);
+                const topIndexed = NumCast(refDoc.y) < NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height) / 2;
                 const deltay = sorted.length > 1 ? NumCast(refDoc.y) - (NumCast(sorted[0].layout.y) + (topIndexed ? 0 : NumCast(sorted[0].layout._height))) : 0;
                 const deltax = sorted.length > 1 ? NumCast(refDoc.x) - NumCast(sorted[0].layout.x) : 0;
 
                 let lastx = NumCast(refDoc.x);
                 let lasty = NumCast(refDoc.y) + (topIndexed ? 0 : NumCast(refDoc._height));
-                setTimeout(
-                    action(() =>
-                        sorted.slice(1).forEach((pair, i) => {
-                            lastx = pair.layout.x = lastx + deltax;
-                            lasty = (pair.layout.y = lasty + deltay) + (topIndexed ? 0 : NumCast(pair.layout._height));
-                        })
-                    )
+                runInAction(() =>
+                    sorted.slice(1).forEach((pair, i) => {
+                        lastx = pair.layout.x = lastx + deltax;
+                        lasty = (pair.layout.y = lasty + deltay) + (topIndexed ? 0 : NumCast(pair.layout._height));
+                    })
                 );
             }
         }
@@ -464,7 +462,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
             if (ptsParent) {
                 const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster);
-                const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
+                const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!);
                 const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
                 const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'alias' : undefined);
                 de.moveDocument = this.props.moveDocument;
@@ -872,7 +870,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
 
         return this.childDocs
-            .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView))
+            .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.()))
             .filter(inkView => inkView?.ComponentView instanceof InkingStroke)
             .map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
             .filter(
@@ -966,7 +964,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.childDocs
             .filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect)
             .forEach(doc => {
-                const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke;
+                const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())?.ComponentView as InkingStroke;
                 const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] };
                 const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point));
                 const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt));
@@ -1121,7 +1119,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     nudge = (x: number, y: number, nudgeTime: number = 500) => {
-        if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) {
+        const collectionDoc = this.props.docViewPath().lastElement().rootDoc;
+        if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._panX !== undefined) {
             this.setPan(
                 NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
                 NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(),
@@ -1256,8 +1255,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 replica={entry.replica}
                 suppressSetHeight={this.layoutEngine ? true : false}
                 renderCutoffProvider={this.renderCutoffProvider}
-                ContainingCollectionView={this.props.CollectionView}
-                ContainingCollectionDoc={this.props.Document}
                 CollectionFreeFormView={this}
                 LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
                 LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
@@ -1779,7 +1776,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const lines = text.split('\n');
                 const height = 30 + 15 * lines.length;
 
-                this.props.ContainingCollectionView?.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
+                this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
             }
         }
     };
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c7d9b6619..97eed7752 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -209,8 +209,6 @@ export class CollectionLinearView extends CollectionSubView() {
                     docFilters={this.props.docFilters}
                     docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                     hideResizeHandles={true}
                 />
             </div>
@@ -251,8 +249,7 @@ export class CollectionLinearView extends CollectionSubView() {
                                 self: this.rootDoc,
                                 _readOnly_: false,
                                 scriptContext: this.props.scriptContext,
-                                thisContainer: this.props.ContainingCollectionDoc,
-                                documentView: this.props.docViewPath().lastElement(),
+                                documentView: this.props.DocumentView?.(),
                             });
                             this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked;
                         })}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b73b1d779..78d3d1b6e 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -262,8 +262,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 docFilters={this.childDocFilters}
                 docRangeFilters={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
-                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                ContainingCollectionView={this.props.CollectionView}
                 dontRegisterView={this.props.dontRegisterView}
                 addDocument={this.props.addDocument}
                 moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 0cca83803..4d61dc272 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -261,8 +261,6 @@ export class CollectionMultirowView extends CollectionSubView() {
                 docFilters={this.childDocFilters}
                 docRangeFilters={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
-                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                ContainingCollectionView={this.props.CollectionView}
                 dontRegisterView={this.props.dontRegisterView}
                 addDocument={this.props.addDocument}
                 moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index fd9bcf681..6d5a73e55 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -793,7 +793,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 fitContentsToBox={returnTrue}
                                 dontCenter={'y'}
                                 onClickScriptDisable="always"
-                                focus={DocUtils.DefaultFocus}
+                                focus={emptyFunction}
                                 renderDepth={this.props.renderDepth + 1}
                                 rootSelected={this.rootSelected}
                                 PanelWidth={this.previewWidthFunc}
@@ -806,8 +806,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 searchFilterDocs={this.searchFilterDocs}
                                 styleProvider={DefaultStyleProvider}
                                 docViewPath={returnEmptyDoclist}
-                                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                                ContainingCollectionView={this.props.CollectionView}
                                 moveDocument={this.props.moveDocument}
                                 addDocument={this.addRow}
                                 removeDocument={this.props.removeDocument}
@@ -849,8 +847,6 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 Document={doc}
                                 DataDoc={dataDoc}
                                 renderDepth={this.props.schema.props.renderDepth + 1}
-                                ContainingCollectionView={this.props.schema.props.CollectionView}
-                                ContainingCollectionDoc={this.props.schema.Document}
                                 PanelWidth={this.tableWidthFunc}
                                 PanelHeight={this.rowHeightFunc}
                                 styleProvider={DefaultStyleProvider}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 2defaae00..856537927 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -29,7 +29,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get schemaDoc() {
-        return this.props.ContainingCollectionDoc!;
+        return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
     }
 
     @computed get rowIndex() {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 13e45963e..5f8ffe8b0 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -28,8 +28,6 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
             docViewPath: returnEmptyDoclist,
-            ContainingCollectionView: undefined,
-            ContainingCollectionDoc: undefined,
             fieldKey: this.props.fieldKey,
             rootSelected: returnFalse,
             isSelected: returnFalse,
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 29e7cd3ad..d703c9595 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -44,25 +44,10 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
         }
 
         const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
-        dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
-            docView.props.removeDocument?.(doc);
-            addDocument(doc);
-            return true;
-        };
-        const containingView = docView.props.ContainingCollectionView;
-        const finishDrag = (e: DragManager.DragCompleteEvent) =>
-            e.docDragData &&
-            (e.docDragData.droppedDocuments = dragData.draggedDocuments.reduce((droppedDocs, d) => {
-                const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView);
-                if (dvs.length) {
-                    dvs.forEach(dv => droppedDocs.push(dv.props.Document));
-                } else {
-                    droppedDocs.push(Doc.MakeAlias(d));
-                }
-                return droppedDocs;
-            }, [] as Doc[]));
+        dragData.canEmbed = true;
+        dragData.moveDocument = (docView.props.docViewPath().lastElement()?.ComponentView as any)?.props.CollectionView?.moveDocument; // this is equal to docView.props.moveDocument, but moveDocument is not a defined prop of a DocumentView .. but maybe it should be?
 
-        DragManager.StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag);
+        DragManager.StartDocumentDrag([dragEle], dragData, downX, downY, undefined);
     }
 }
 
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 7bdace2b6..5f2d4a7b6 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -85,15 +85,13 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         PanelWidth={this.getPWidth}
                         PanelHeight={this.getPHeight}
                         renderDepth={0}
-                        focus={DocUtils.DefaultFocus}
+                        focus={emptyFunction}
                         docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
                         docFilters={returnEmptyFilter}
                         docRangeFilters={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
-                        ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
                     />
                 </div>
             </div>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 9bdb2cee7..24b9f3b25 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -160,8 +160,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     @action public float = () => {
-        const { Document: topDoc, ContainingCollectionView: container } = this.props;
-        const screenXf = container?.screenToLocalTransform();
+        const topDoc = this.rootDoc;
+        const containerDocView = this.props.docViewPath().lastElement();
+        const screenXf = containerDocView?.screenToLocalTransform();
         if (screenXf) {
             SelectionManager.DeselectAll();
             if (topDoc.z) {
@@ -178,7 +179,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 topDoc.x = fpt[0];
                 topDoc.y = fpt[1];
             }
-            setTimeout(() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, container)!, false), 0);
+            setTimeout(() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, containerDocView), false), 0);
         }
     };
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index dbcfe43cf..76a5ce7b3 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -154,7 +154,6 @@ export class DocumentContentsView extends React.Component<
             // these are the properties in  DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
             'hideResizeHandles',
             'hideTitle',
-            'treeViewDoc',
             'contentPointerEvents',
             'radialMenu',
             'LayoutTemplateString',
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index df3299eef..47705d53d 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -142,7 +142,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             } else if (startLink !== endLink) {
                 endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
                 startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
-                const linkDoc = DocUtils.MakeLink(startLink, endLink, { linkRelationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined }, undefined, true);
+                const linkDoc = DocUtils.MakeLink(startLink, endLink, { linkRelationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });
 
                 LinkManager.currentLink = linkDoc;
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f161a7b9b..be07f1fe5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -148,8 +148,6 @@ export interface DocumentViewSharedProps {
     DataDoc?: Doc;
     contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
     fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
-    ContainingCollectionView: Opt<CollectionView>;
-    ContainingCollectionDoc: Opt<Doc>;
     suppressSetHeight?: boolean;
     thumbShown?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
@@ -179,6 +177,7 @@ export interface DocumentViewSharedProps {
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     canEmbedOnDrag?: boolean;
+    treeViewDoc?: Doc;
     xPadding?: number;
     yPadding?: number;
     dropAction?: dropActionType;
@@ -210,7 +209,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     hideOpenButton?: boolean;
     hideDeleteButton?: boolean;
     hideLinkAnchors?: boolean;
-    treeViewDoc?: Doc;
     isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
     isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
     contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
@@ -429,7 +427,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             this: this.layoutDoc,
                             self: this.rootDoc,
                             scriptContext: this.props.scriptContext,
-                            thisContainer: this.props.ContainingCollectionDoc,
                             documentView: this.props.DocumentView(),
                             clientX, clientY,  altKey,  shiftKey,  ctrlKey,
                             value: undefined,
@@ -456,7 +453,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                                 self: this.rootDoc,
                                 _readOnly_: false,
                                 scriptContext: this.props.scriptContext,
-                                thisContainer: this.props.ContainingCollectionDoc,
                                 documentView: this.props.DocumentView(),
                                 clientX,
                                 clientY,
@@ -605,7 +601,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 }
                 if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
                     const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
-                    de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, undefined, [de.x, de.y - 50]);
+                    de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                 }
             }
         }
@@ -697,7 +693,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
             !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
 
-            if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+            if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.docViewPath().lastElement()?.rootDoc?._viewType !== CollectionViewType.Tree) {
                 const existingOnClick = cm.findByDescription('OnClick...');
                 const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
 
@@ -1446,7 +1442,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
     docViewPathFunc = () => this.docViewPath;
     isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
-    select = (extendSelection: boolean) => SelectionManager.SelectView(this, !SelectionManager.Views().some(v => v.props.Document === this.props.ContainingCollectionDoc) && extendSelection);
+    select = (extendSelection: boolean) => SelectionManager.SelectView(this, extendSelection);
     NativeWidth = () => this.effectiveNativeWidth;
     NativeHeight = () => this.effectiveNativeHeight;
     PanelWidth = () => this.panelWidth;
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8d3534a5c..86779e0dd 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -32,6 +32,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields
     // Also, see InkingStroke for examples of creating text boxes from render() methods which set some of these fields
     backgroundColor?: string;
+    treeViewDoc?: Doc;
     color?: string;
     fontSize?: number;
     height?: number;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 94434dce7..7ea6d42ff 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -61,8 +61,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
             docViewPath: returnEmptyDoclist,
-            ContainingCollectionView: undefined,
-            ContainingCollectionDoc: undefined,
             fieldKey: this.props.keyName,
             rootSelected: returnFalse,
             isSelected: returnFalse,
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index fcc5b6975..c58b5dd8c 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -271,14 +271,12 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             docFilters={returnEmptyFilter}
                             docRangeFilters={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
-                            ContainingCollectionDoc={undefined}
-                            ContainingCollectionView={undefined}
                             renderDepth={0}
                             suppressSetHeight={true}
                             PanelWidth={this.width}
                             PanelHeight={this.height}
                             pointerEvents={returnNone}
-                            focus={DocUtils.DefaultFocus}
+                            focus={emptyFunction}
                             whenChildContentsActiveChanged={returnFalse}
                             ignoreAutoHeight={true} // need to ignore autoHeight otherwise autoHeight text boxes will expand beyond the preview panel size.
                             bringToFront={returnFalse}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index db11a7776..e015024fd 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -305,8 +305,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                             addDocument={returnFalse}
                             CollectionView={undefined}
                             ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                            renderDepth={this.props.renderDepth + 1}
-                            ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                            renderDepth={this.props.renderDepth + 1}>
                             <>
                                 {this.threed}
                                 {this.content}
@@ -330,7 +329,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                                 moveDocument={returnFalse}
                                 addDocument={returnFalse}
                                 renderDepth={this.props.renderDepth + 1}
-                                ContainingCollectionDoc={this.props.ContainingCollectionDoc}></FormattedTextBox>
+                            />
                         )}
                     </div>
                 </div>
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 8eacfbc51..1a75a7e76 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -568,12 +568,12 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
     if (selectedViews.length) {
         if (checkResult) {
             const selView = selectedViews.lastElement();
-            const layoutFrameNumber = Cast(selView.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
             const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
             return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber).backgroundColor ?? 'transparent';
         }
         selectedViews.forEach(dv => {
-            const layoutFrameNumber = Cast(dv.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
             const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
             if (contentFrameNumber !== undefined) {
                 CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { backgroundColor: color });
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index c00ab6a7e..b31fc01ff 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -9,10 +9,9 @@ import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { ColorScheme } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
-import { DocumentView } from '../DocumentView';
+import { DocFocusOptions, DocumentView } from '../DocumentView';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
-import { SelectionManager } from '../../../util/SelectionManager';
 
 export class DashDocView {
     dom: HTMLSpanElement; // container for label and value
@@ -151,7 +150,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._spanRef.current);
         return new Transform(-translateX, -translateY, 1).scale(1 / scale);
     };
-    outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document, {}); // ideally, this would scroll to show the focus target
+    outerFocus = (target: Doc, options: DocFocusOptions) => this._textBox.focus(target, options); // ideally, this would scroll to show the focus target
 
     onKeyDown = (e: any) => {
         e.stopPropagation();
@@ -212,8 +211,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     docFilters={this.props.tbox?.props.docFilters}
                     docRangeFilters={this.props.tbox?.props.docRangeFilters}
                     searchFilterDocs={this.props.tbox?.props.searchFilterDocs}
-                    ContainingCollectionView={this._textBox.props.ContainingCollectionView}
-                    ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                 />
             </div>
         );
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index f23426bb3..c43206629 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -235,10 +235,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     };
 
     createPivotForField = (e: React.MouseEvent) => {
-        let container = this.props.tbox.props.ContainingCollectionView;
-        while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) {
-            container = container.props.ContainingCollectionView;
-        }
+        let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement();
         if (container) {
             const alias = Doc.MakeAlias(container.props.Document);
             alias._viewType = CollectionViewType.Time;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 677c4662b..bc2a5d797 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -541,7 +541,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 // embed document when dragg marked as embed
             } else if (de.embedKey) {
                 const target = dragData.droppedDocuments[0];
-                target._fitContentsToBox = true;
                 const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](),
                     height: target[HeightSym](),
@@ -552,6 +551,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (!['alias', 'copy'].includes((dragData.dropAction ?? '') as any)) {
                     dragData.removeDocument?.(dragData.draggedDocuments[0]);
                 }
+                target._fitContentsToBox = true;
+                target.context = this.rootDoc;
                 const view = this._editorView!;
                 view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
                 e.stopPropagation();
@@ -880,7 +881,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (this._editorView && this._recordingStart) {
             if (this._break) {
                 const textanchorFunc = () => {
-                    const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor' });
+                    const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor', unrendered: true });
                     return this.addDocument(tanch) ? tanch : undefined;
                 };
                 const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement();
@@ -956,7 +957,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             let hadStart = start !== 0;
             frag.forEach((node, index) => {
                 const examinedNode = findAnchorNode(node, editor);
-                if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) {
+                if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) {
                     nodes.push(examinedNode.node);
                     !hadStart && (start = index + examinedNode.start);
                     hadStart = true;
@@ -971,9 +972,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 }
                 return undefined;
             }
+            if (node.type === this._editorView?.state.schema.nodes.dashDoc) {
+                if (node.attrs.docId === textAnchorId) {
+                    return { node, start: 0 };
+                }
+                return undefined;
+            }
             if (!node.isText) {
                 const content = findAnchorFrag(node.content, editor);
-                return { node: node.copy(content.frag), start: content.start };
+                if (content.frag.childCount) return { node: content.frag.childCount ? content.frag.child(0) : node, start: content.start };
             }
             const marks = [...node.marks];
             const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor);
@@ -987,7 +994,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const ret = findAnchorFrag(editor.state.doc.content, editor);
 
             const content = (ret.frag as any)?.content;
-            if ((ret.frag.size > 2 || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) {
+            if ((ret.frag.size || (content?.length && content[0].type === this._editorView.state.schema.nodes.dashDoc) || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) {
                 !options.instant && (this._focusSpeed = focusSpeed);
                 let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
                 if (ret.frag.firstChild) {
@@ -998,6 +1005,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: 'yellow', transform: 'scale(3)', 'transform-origin': 'left bottom' });
                 setTimeout(() => (this._focusSpeed = undefined), this._focusSpeed);
                 setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000));
+                return focusSpeed;
+            } else {
+                return this.props.focus(this.rootDoc, options);
             }
         }
     };
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 3589a9065..807a19771 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -2457,7 +2457,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         {mode !== CollectionViewType.Invalid ? (
                             <CollectionView
                                 {...this.props}
-                                ContainingCollectionDoc={this.props.Document}
                                 PanelWidth={this.props.PanelWidth}
                                 PanelHeight={this.panelHeight}
                                 childIgnoreNativeSize={true}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 92696240b..502eef373 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -39,7 +39,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // Idea: this boolean will determine whether to automatically show the video when this preselement is selected.
     // @observable static showVideo: boolean = false;
     @computed get indexInPres() {
-        return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc);
+        return DocListCast(this.presBox?.[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.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;
@@ -51,11 +51,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.presBoxView?.selectedArray;
     }
     @computed get presBoxView() {
-        const vpath = this.props.docViewPath();
-        return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as PresBox) : undefined;
+        return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as PresBox;
     }
     @computed get presBox() {
-        return this.props.ContainingCollectionDoc!;
+        return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
     }
     @computed get targetDoc() {
         return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc;
@@ -110,14 +109,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     docFilters={this.props.docFilters}
                     docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                     rootSelected={returnTrue}
                     addDocument={returnFalse}
                     removeDocument={returnFalse}
                     fitContentsToBox={returnTrue}
                     moveDocument={this.props.moveDocument!}
-                    focus={DocUtils.DefaultFocus}
+                    focus={emptyFunction}
                     whenChildContentsActiveChanged={returnFalse}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
@@ -195,7 +192,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
         if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
         dragData.dropAction = 'move';
-        dragData.treeViewDoc = this.presBox._viewType === CollectionViewType.Tree ? this.props.ContainingCollectionDoc : undefined; // this.props.DocumentView?.()?.props.treeViewDoc;
+        dragData.treeViewDoc = this.presBox?._viewType === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc;
         dragData.moveDocument = this.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
@@ -269,7 +266,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     removeItem = action((e: React.MouseEvent) => {
         e.stopPropagation();
-        if (this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
+        if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
             this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
         }
         this.props.removeDocument?.(this.rootDoc);
@@ -406,15 +403,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed
     get toolbarWidth(): number {
         const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
-        let width: number = NumCast(this.presBox._width);
+        let width: number = NumCast(this.presBox?._width);
         if (presBoxDocView) width = presBoxDocView.props.PanelWidth();
         if (width === 0) width = 300;
         return width;
     }
 
     @computed get presButtons() {
-        const presBox: Doc = this.presBox; //presBox
-        const presBoxColor: string = StrCast(presBox._backgroundColor);
+        const presBox = this.presBox; //presBox
+        const presBoxColor: string = StrCast(presBox?._backgroundColor);
         const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
         const targetDoc: Doc = this.targetDoc;
         const activeItem: Doc = this.rootDoc;
@@ -494,10 +491,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get mainItem() {
         const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false;
-        const isCurrent: boolean = this.presBox._itemIndex === this.indexInPres;
+        const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres;
         const miniView: boolean = this.toolbarWidth <= 110;
-        const presBox: Doc = this.presBox; //presBox
-        const presBoxColor: string = StrCast(presBox._backgroundColor);
+        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;
 
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index f2e9be61d..d63c25dbe 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -46,7 +46,7 @@ export class TopBar extends React.Component {
         return (
             <div className="topbar-left">
                 {Doc.ActiveDashboard ? (
-                    <IconButton onClick={this.navigateToHome} icon={<FontAwesomeIcon icon="home" />} isCircle={true} hoverStyle="gray" color={this.textColor} />
+                    <IconButton onClick={this.navigateToHome} icon={<FontAwesomeIcon icon="home" />} color={this.textColor} />
                 ) : (
                     <div className="logo-container">
                         <img className="logo" src="/assets/medium-blue-light-blue-circle.png" alt="dash logo"></img>
@@ -55,18 +55,7 @@ export class TopBar extends React.Component {
                     </div>
                 )}
                 {Doc.ActiveDashboard && (
-                    <Button
-                        text="Explore"
-                        tooltip="Browsing mode for directly navigating to documents"
-                        fontSize={FontSize.SECONDARY}
-                        isActive={MainView.Instance._exploreMode}
-                        size={Size.SMALL}
-                        color={this.textColor}
-                        borderRadius={5}
-                        hoverStyle="gray"
-                        iconPosition="right"
-                        onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))}
-                    />
+                    <Button text="Explore" tooltip="Browsing mode for directly navigating to documents" size={Size.SMALL} color={this.textColor} onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))} />
                 )}
             </div>
         );
@@ -94,13 +83,8 @@ export class TopBar extends React.Component {
                 <Button
                     text={StrCast(Doc.ActiveDashboard.title)}
                     tooltip="Browsing mode for directly navigating to documents"
-                    fontSize={FontSize.SECONDARY}
                     size={Size.SMALL}
                     color={'white'}
-                    type="outline"
-                    backgroundColor={Colors.MEDIUM_BLUE}
-                    borderRadius={5}
-                    hoverStyle="none"
                     onClick={(e: React.MouseEvent) => {
                         const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard);
                         ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' });
@@ -121,21 +105,13 @@ export class TopBar extends React.Component {
                     onClick={() => {
                         SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
                     }}
-                    type="outline"
-                    fontSize={FontSize.SECONDARY}
                     size={Size.SMALL}
-                    borderRadius={5}
-                    hoverStyle="gray"
                 />
                 {!Doc.noviceMode && (
                     <IconButton
-                        fontSize={FontSize.SECONDARY}
-                        isCircle={true}
                         tooltip="Work on a copy of the dashboard layout"
                         size={Size.SMALL}
                         color={this.textColor}
-                        borderRadius={Borders.STANDARD}
-                        hoverStyle="gray"
                         onClick={async () => {
                             const batch = UndoManager.StartBatch('snapshot');
                             await DashboardView.snapshotDashboard();
@@ -156,26 +132,9 @@ export class TopBar extends React.Component {
     @computed get topbarRight() {
         return (
             <div className="topbar-right">
-                <IconButton size={Size.SMALL} isCircle={true} color={Colors.LIGHT_GRAY} backgroundColor={Colors.DARK_GRAY} hoverStyle="gray" onClick={() => ReportManager.Instance.open()} icon={<FaBug />} />
-                <IconButton
-                    size={Size.SMALL}
-                    isCircle={true}
-                    color={Colors.LIGHT_GRAY}
-                    backgroundColor={Colors.DARK_GRAY}
-                    hoverStyle="gray"
-                    onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')}
-                    icon={<FontAwesomeIcon icon="question-circle" />}
-                />
-                <IconButton
-                    size={Size.SMALL}
-                    isCircle={true}
-                    color={Colors.LIGHT_GRAY}
-                    backgroundColor={Colors.DARK_GRAY}
-                    hoverStyle="gray"
-                    isActive={SettingsManager.Instance.isOpen}
-                    onClick={() => SettingsManager.Instance.open()}
-                    icon={<FontAwesomeIcon icon="cog" />}
-                />
+                <IconButton size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={() => ReportManager.Instance.open()} icon={<FaBug />} />
+                <IconButton size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
+                <IconButton size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={() => SettingsManager.Instance.open()} icon={<FontAwesomeIcon icon="cog" />} />
                 {/* <Button text={'Logout'} borderRadius={5} hoverStyle={'gray'} backgroundColor={Colors.DARK_GRAY} color={this.textColor} fontSize={FontSize.SECONDARY} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> */}
             </div>
         );
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 64baa351c..a0b437354 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -13,13 +13,18 @@ import { listSpec } from '../fields/Schema';
 import { Cast, FieldValue } from '../fields/Types';
 import { nullAudio } from '../fields/URLField';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, emptyPath } from '../Utils';
-import "./ImageUpload.scss";
+import './ImageUpload.scss';
 import { MobileInterface } from './MobileInterface';
 import React = require('react');
 
 @observer
 export class AudioUpload extends React.Component {
-    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+    @observable public _audioCol: Doc = FieldValue(
+        Cast(
+            Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: 'mobile audio', _width: 500, _height: 100 }), Doc) as Doc], { title: 'mobile audio', _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: '0 0' }),
+            Doc
+        )
+    ) as Doc;
 
     /**
      * Handles the onclick functionality for the 'Restart' button
@@ -28,25 +33,36 @@ export class AudioUpload extends React.Component {
     @action
     clearUpload = () => {
         for (let i = 1; i < 8; i++) {
-            this.setOpacity(i, "0.2");
+            this.setOpacity(i, '0.2');
         }
-        this._audioCol = FieldValue(Cast(
-            Docs.Create.FreeformDocument(
-                [Cast(Docs.Create.AudioDocument(nullAudio, {
-                    title: "mobile audio",
-                    _width: 500,
-                    _height: 100
-                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
-    }
+        this._audioCol = FieldValue(
+            Cast(
+                Docs.Create.FreeformDocument(
+                    [
+                        Cast(
+                            Docs.Create.AudioDocument(nullAudio, {
+                                title: 'mobile audio',
+                                _width: 500,
+                                _height: 100,
+                            }),
+                            Doc
+                        ) as Doc,
+                    ],
+                    { title: 'mobile audio', _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: '0 0' }
+                ),
+                Doc
+            )
+        ) as Doc;
+    };
 
-    /** 
+    /**
      * Handles the onClick of the 'Close' button
      * Reset upload interface and toggle audio
      */
     closeUpload = () => {
         this.clearUpload();
         MobileInterface.Instance.toggleAudio();
-    }
+    };
 
     /**
      * Handles the on click of the 'Upload' button.
@@ -57,14 +73,14 @@ export class AudioUpload extends React.Component {
         const audioDoc = this._audioCol;
         const data = Cast(audioRightSidebar.data, listSpec(Doc));
         for (let i = 1; i < 8; i++) {
-            setTimeout(() => this.setOpacity(i, "1"), i * 200);
+            setTimeout(() => this.setOpacity(i, '1'), i * 200);
         }
         if (data) {
             data.push(audioDoc);
         }
         // Resets uploader after 3 seconds
         setTimeout(this.clearUpload, 3000);
-    }
+    };
 
     // Returns the upload audio menu
     private get uploadInterface() {
@@ -72,11 +88,13 @@ export class AudioUpload extends React.Component {
             <>
                 <ContextMenu />
                 <DictationOverlay />
-                <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
+                <div style={{ display: 'none' }}>
+                    <RichTextMenu key="rich" />
+                </div>
                 <div className="closeUpload" onClick={() => this.closeUpload()}>
-                    <FontAwesomeIcon icon="window-close" size={"lg"} />
+                    <FontAwesomeIcon icon="window-close" size={'lg'} />
                 </div>
-                <FontAwesomeIcon icon="microphone" size="lg" style={{ fontSize: "130" }} />
+                <FontAwesomeIcon icon="microphone" size="lg" style={{ fontSize: '130' }} />
                 <div className="audioUpload_cont">
                     <DocumentView
                         Document={this._audioCol}
@@ -96,12 +114,10 @@ export class AudioUpload extends React.Component {
                         isDocumentActive={returnTrue}
                         isContentActive={emptyFunction}
                         focus={emptyFunction}
-                        styleProvider={() => "rgba(0,0,0,0)"}
+                        styleProvider={() => 'rgba(0,0,0,0)'}
                         docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-                        ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
                     />
                 </div>
                 <div className="restart_label" onClick={this.clearUpload}>
@@ -125,28 +141,16 @@ export class AudioUpload extends React.Component {
 
     // Handles the setting of the loading bar
     setOpacity = (index: number, opacity: string) => {
-        const slab = document.getElementById("slab0" + index);
+        const slab = document.getElementById('slab0' + index);
         if (slab) {
             slab.style.opacity = opacity;
         }
-    }
+    };
 
     @observable private dialogueBoxOpacity = 1;
     @observable private overlayOpacity = 0.4;
 
     render() {
-        return (
-            <MainViewModal
-                contents={this.uploadInterface}
-                isDisplayed={true}
-                interactive={true}
-                dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
-                overlayDisplayedOpacity={this.overlayOpacity}
-                closeOnExternalClick={this.closeUpload}
-            />
-        );
+        return <MainViewModal contents={this.uploadInterface} isDisplayed={true} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.closeUpload} />;
     }
-
 }
-
-
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 2ae597b0b..a543c2f70 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -397,7 +397,7 @@ export class MobileInterface extends React.Component {
                     renderDepth={0}
                     isDocumentActive={returnTrue}
                     isContentActive={emptyFunction}
-                    focus={DocUtils.DefaultFocus}
+                    focus={emptyFunction}
                     styleProvider={this.whitebackground}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
@@ -405,8 +405,6 @@ export class MobileInterface extends React.Component {
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
                 />
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From 99326044a2d81a9f0d0312b4d209946e0f119b78 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 24 Apr 2023 17:47:54 -0400
Subject: crappy version of schema image cell preview on hover

---
 .../collections/collectionSchema/SchemaRowBox.tsx  |  2 +-
 .../collectionSchema/SchemaTableCell.tsx           | 65 ++++++++++++++--------
 2 files changed, 43 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 59b571b58..79808d8f8 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -88,7 +88,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="schema-row"
-                style={{ height: CollectionSchemaView._rowHeight, backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined, pointerEvents: this.schemaView?.props.isContentActive() ? 'all' : undefined }}
+                style={{ height: CollectionSchemaView._rowHeight, backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined }}
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 4e31b1e1e..2325339fc 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -58,7 +58,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
         };
 
         return (
-            <div className="schemacell-edit-wrapper">
+            <div className="schemacell-edit-wrapper" style={{ cursor: !this.readOnly ? 'text' : 'default', pointerEvents: !this.readOnly && this.props.isRowActive() ? 'all' : 'none' }}>
                 <EditableView
                     contents={<FieldView {...props} />}
                     GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
@@ -76,9 +76,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
 
     getCellWithContent(content: any) {
         return (
-            <div
-                className="schema-table-cell"
-                style={this.props.isRowActive() && !this.readOnly ? { width: this.props.columnWidth, cursor: 'text', pointerEvents: 'auto' } : { width: this.props.columnWidth, cursor: 'default', pointerEvents: 'none' }}>
+            <div className="schema-table-cell" style={this.props.isRowActive() && !this.readOnly ? { width: this.props.columnWidth } : { width: this.props.columnWidth }}>
                 {content}
             </div>
         );
@@ -107,7 +105,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             case ColumnType.Image:
                 return <SchemaImageCell {...this.props} />;
             case ColumnType.Date:
-                return <SchemaDateCell {...this.props} />;
+            // return <SchemaDateCell {...this.props} />;
             default:
                 return this.getCellWithContent(this.defaultCellContent);
         }
@@ -117,16 +115,18 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
 // mj: most of this is adapted from old schema code so I'm not sure what it does tbh
 @observer
 export class SchemaImageCell extends SchemaTableCell {
+    @observable _previewRef: HTMLImageElement | undefined;
+
     choosePath(url: URL) {
         if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question
 
         const ext = extname(url.href);
-        return url.href.replace(ext, '_o' + ext);
+        return url.href.replace(ext, '_s' + ext);
     }
 
-    get content() {
+    get url() {
         const field = Cast(this.props.Document[this.props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
         const alts = DocListCast(this.props.Document[this.props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
         const altpaths = alts
@@ -136,13 +136,42 @@ export class SchemaImageCell extends SchemaTableCell {
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
         // If there is a path, follow it; otherwise, follow a link to a default image icon
         const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
+        return url[0];
+    }
+
+    @action
+    showHoverPreview = (e: React.PointerEvent) => {
+        this._previewRef = document.createElement('img');
+        document.body.appendChild(this._previewRef);
+        const ext = extname(this.url);
+        this._previewRef.src = this.url.replace('_s' + ext, '_m' + ext);
+        this._previewRef.style.position = 'absolute';
+        this._previewRef.style.left = e.clientX + 10 + 'px';
+        this._previewRef.style.top = e.clientY + 10 + 'px';
+        this._previewRef.style.zIndex = '1000';
+    };
+
+    @action
+    moveHoverPreview = (e: React.PointerEvent) => {
+        if (!this._previewRef) return;
+        this._previewRef.style.left = e.clientX + 10 + 'px';
+        this._previewRef.style.top = e.clientY + 10 + 'px';
+    };
 
+    @action
+    removeHoverPreview = (e: React.PointerEvent) => {
+        if (!this._previewRef) return;
+        document.body.removeChild(this._previewRef);
+    };
+
+    get content() {
         const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio
-        let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px
-        const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
-        width = height * aspect; // increase the width of the image if necessary to maintain proportionality
+        // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px
+        // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
+        const height = CollectionSchemaView._rowHeight - 10;
+        const width = height * aspect; // increase the width of the image if necessary to maintain proportionality
 
-        return <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} />;
+        return <img src={this.url} width={width} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
     }
 
     render() {
@@ -171,18 +200,8 @@ export class SchemaDateCell extends SchemaTableCell {
     };
 
     get content() {
-        return !this._pickingDate ? (
-            <div onPointerDown={action(() => (this._pickingDate = true))}>{this.defaultCellContent}</div>
-        ) : (
-            <DatePicker
-                selected={this.date.date}
-                onSelect={(date: any) => {
-                    this.handleChange(date);
-                    this._pickingDate = false;
-                }}
-                onChange={(date: any) => this.handleChange(date)}
-            />
-        );
+        return <DatePicker dateFormat={'Pp'} selected={this.date.date} onChange={(date: any) => this.handleChange(date)} />;
+        // return !this._pickingDate ? <div onPointerDown={action(() => (this._pickingDate = true))}>{this.defaultCellContent}</div> : <DatePicker dateFormat={'Pp'} selected={this.date.date} onChange={(date: any) => this.handleChange(date)} />;
     }
 
     render() {
-- 
cgit v1.2.3-70-g09d2


From bc550e43b6042176cf59f04cbff51e1b21adaa8a Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 27 Apr 2023 00:41:43 -0400
Subject: trying to get schema keyboard controls working

---
 .../collectionSchema/CollectionSchemaView.tsx      | 43 ++++++++++++--
 .../collections/collectionSchema/SchemaRowBox.tsx  |  1 +
 .../collectionSchema/SchemaTableCell.tsx           | 65 +++++++++++++---------
 3 files changed, 78 insertions(+), 31 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f6dc2a3d7..92a04f5ec 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -74,6 +74,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _menuValue: string = '';
     @observable _filterColumnIndex: number | undefined;
     @observable _filterSearchValue: string = '';
+    @observable _selectedCell: [Doc, number] | undefined;
 
     get fieldInfos() {
         const docs = this.childDocs;
@@ -174,8 +175,11 @@ export class CollectionSchemaView extends CollectionSubView() {
                             !e.shiftKey && this.clearSelection();
                             const newDoc = this.sortedDocs.docs[lastIndex + 1];
                             if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
-                            else this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
-                            this.scrollToDoc(newDoc, {});
+                            else {
+                                this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
+                                this._selectedCell && (this._selectedCell[0] = newDoc);
+                                this.scrollToDoc(newDoc, {});
+                            }
                         }
                         e.stopPropagation();
                         e.preventDefault();
@@ -191,13 +195,37 @@ export class CollectionSchemaView extends CollectionSubView() {
                             !e.shiftKey && this.clearSelection();
                             const newDoc = this.sortedDocs.docs[firstIndex - 1];
                             if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
-                            else this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
-                            this.scrollToDoc(newDoc, {});
+                            else {
+                                this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
+                                this._selectedCell && (this._selectedCell[0] = newDoc);
+                                this.scrollToDoc(newDoc, {});
+                            }
                         }
                         e.stopPropagation();
                         e.preventDefault();
                     }
                     break;
+                case 'ArrowRight':
+                    if (this._selectedCell) {
+                        this._selectedCell[1] = Math.min(this._selectedCell[1] + 1, this.columnKeys.length - 1);
+                    } else if (this._selectedDocs.length > 0) {
+                        this.selectCell(this._selectedDocs[0], 0);
+                    }
+                    break;
+                case 'ArrowLeft':
+                    if (this._selectedCell) {
+                        this._selectedCell[1] = Math.max(this._selectedCell[1] - 1, 0);
+                    } else if (this._selectedDocs.length > 0) {
+                        this.selectCell(this._selectedDocs[0], 0);
+                    }
+                    break;
+                case 'Backspace': {
+                    this.removeDocument(this._selectedDocs);
+                    break;
+                }
+                case 'Escape': {
+                    this._selectedCell = undefined;
+                }
             }
         }
     };
@@ -392,6 +420,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         }
     };
 
+    @action
+    selectCell = (doc: Doc, index: number) => {
+        this._selectedCell = [doc, index];
+    };
+
     sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc));
 
     setDropIndex = (index: number) => (this._closestDropIndex = index);
@@ -428,7 +461,7 @@ export class CollectionSchemaView extends CollectionSubView() {
             const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs];
             const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc));
             this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...removed, ...draggedDocs, ...pushedDocs]);
-            SelectionManager.DeselectAll();
+            this.clearSelection();
             draggedDocs.forEach(doc => {
                 const draggedView = DocumentManager.Instance.getFirstDocumentView(doc);
                 if (draggedView) DocumentManager.Instance.RemoveView(draggedView);
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 79808d8f8..f5a16cec0 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -123,6 +123,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <SchemaTableCell
                             key={key}
                             Document={this.rootDoc}
+                            col={index}
                             schemaView={this.schemaView}
                             fieldKey={key}
                             columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 2325339fc..2b61ea261 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -11,13 +11,16 @@ import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSc
 import './CollectionSchemaView.scss';
 import { action, computed, observable } from 'mobx';
 import { extname } from 'path';
-import { Cast, DateCast } from '../../../../fields/Types';
+import { Cast, DateCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
 import { DateField } from '../../../../fields/DateField';
 import DatePicker from 'react-datepicker';
+import { Colors } from '../../global/globalEnums';
+import { SelectionManager } from '../../../util/SelectionManager';
 
 export interface SchemaTableCellProps {
     Document: Doc;
+    col: number;
     schemaView: CollectionSchemaView | undefined;
     fieldKey: string;
     columnWidth: number;
@@ -27,10 +30,23 @@ export interface SchemaTableCellProps {
 
 @observer
 export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
-    get readOnly() {
+    private _editorRef: EditableView | null = null;
+
+    @computed get readOnly() {
         return this.props.schemaView?.fieldInfos[this.props.fieldKey]?.readOnly ?? false;
     }
 
+    @computed get selected() {
+        const selected: [Doc, number] | undefined = this.props.schemaView?._selectedCell;
+        return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col;
+    }
+
+    componentDidUpdate() {
+        if (!this.selected) {
+            this._editorRef?.setIsFocused(false);
+        }
+    }
+
     get defaultCellContent() {
         const props: FieldViewProps = {
             Document: this.props.Document,
@@ -60,6 +76,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
         return (
             <div className="schemacell-edit-wrapper" style={{ cursor: !this.readOnly ? 'text' : 'default', pointerEvents: !this.readOnly && this.props.isRowActive() ? 'all' : 'none' }}>
                 <EditableView
+                    ref={ref => (this._editorRef = ref)}
                     contents={<FieldView {...props} />}
                     GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
                     SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => {
@@ -68,20 +85,12 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                         }
                         return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value);
                     }}
-                    editing={this.props.isRowActive() ? undefined : false}
+                    editing={this.selected ? undefined : false}
                 />
             </div>
         );
     }
 
-    getCellWithContent(content: any) {
-        return (
-            <div className="schema-table-cell" style={this.props.isRowActive() && !this.readOnly ? { width: this.props.columnWidth } : { width: this.props.columnWidth }}>
-                {content}
-            </div>
-        );
-    }
-
     get getCellType() {
         const columnTypeStr = this.props.schemaView?.fieldInfos[this.props.fieldKey]?.fieldType;
         if (columnTypeStr) {
@@ -99,7 +108,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
         return ColumnType.Any;
     }
 
-    render() {
+    get content() {
         const cellType: ColumnType = this.getCellType;
         switch (cellType) {
             case ColumnType.Image:
@@ -107,14 +116,27 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             case ColumnType.Date:
             // return <SchemaDateCell {...this.props} />;
             default:
-                return this.getCellWithContent(this.defaultCellContent);
+                return this.defaultCellContent;
         }
     }
+
+    render() {
+        return (
+            <div
+                className="schema-table-cell"
+                onPointerDown={action(e => {
+                    if (!this.selected) this.props.schemaView?.selectCell(this.props.Document, this.props.col);
+                })}
+                style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
+                {this.content}
+            </div>
+        );
+    }
 }
 
 // mj: most of this is adapted from old schema code so I'm not sure what it does tbh
 @observer
-export class SchemaImageCell extends SchemaTableCell {
+export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
     @observable _previewRef: HTMLImageElement | undefined;
 
     choosePath(url: URL) {
@@ -164,7 +186,7 @@ export class SchemaImageCell extends SchemaTableCell {
         document.body.removeChild(this._previewRef);
     };
 
-    get content() {
+    render() {
         const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio
         // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px
         // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
@@ -173,14 +195,10 @@ export class SchemaImageCell extends SchemaTableCell {
 
         return <img src={this.url} width={width} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
     }
-
-    render() {
-        return this.getCellWithContent(this.content);
-    }
 }
 
 @observer
-export class SchemaDateCell extends SchemaTableCell {
+export class SchemaDateCell extends React.Component<SchemaTableCellProps> {
     @observable _pickingDate: boolean = false;
 
     @computed get date(): DateField {
@@ -199,12 +217,7 @@ export class SchemaDateCell extends SchemaTableCell {
         //}
     };
 
-    get content() {
-        return <DatePicker dateFormat={'Pp'} selected={this.date.date} onChange={(date: any) => this.handleChange(date)} />;
-        // return !this._pickingDate ? <div onPointerDown={action(() => (this._pickingDate = true))}>{this.defaultCellContent}</div> : <DatePicker dateFormat={'Pp'} selected={this.date.date} onChange={(date: any) => this.handleChange(date)} />;
-    }
-
     render() {
-        return this.getCellWithContent(this.content);
+        return <DatePicker dateFormat={'Pp'} selected={this.date.date} onChange={(date: any) => this.handleChange(date)} />;
     }
 }
-- 
cgit v1.2.3-70-g09d2


From b82a909a63a6de414d075735453240ebc02f5aa3 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 27 Apr 2023 13:20:52 -0400
Subject: fixed editing schema cells with keyboard only

---
 src/client/views/EditableView.tsx                  |  6 +++++
 .../collectionSchema/CollectionSchemaView.tsx      | 19 +++++++++------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  6 +++--
 .../collectionSchema/SchemaTableCell.tsx           | 28 ++++++++++++++++------
 4 files changed, 43 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 164b6c57a..d1311a60a 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -106,6 +106,12 @@ export class EditableView extends React.Component<EditableProps> {
             case ':':
                 this.props.menuCallback?.(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
                 break;
+            case 'ArrowUp':
+            case 'ArrowDown':
+            case 'ArrowLeft':
+            case 'ArrowRight':
+                e.stopPropagation();
+                break;
             case 'Shift':
             case 'Alt':
             case 'Meta':
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 92a04f5ec..8cd307adf 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,6 +1,6 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, ObservableMap, untracked } from 'mobx';
+import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc';
@@ -170,12 +170,12 @@ export class CollectionSchemaView extends CollectionSubView() {
                         const lastDoc = this._selectedDocs.lastElement();
                         const lastIndex = this.rowIndex(lastDoc);
                         const curDoc = this.sortedDocs.docs[lastIndex];
-                        // const curDoc = this.childDocs[lastIndex];
                         if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
                             !e.shiftKey && this.clearSelection();
                             const newDoc = this.sortedDocs.docs[lastIndex + 1];
-                            if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
-                            else {
+                            if (this._selectedDocs.includes(newDoc)) {
+                                SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+                            } else {
                                 this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
                                 this._selectedCell && (this._selectedCell[0] = newDoc);
                                 this.scrollToDoc(newDoc, {});
@@ -189,7 +189,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     {
                         const firstDoc = this._selectedDocs.lastElement();
                         const firstIndex = this.rowIndex(firstDoc);
-                        // const curDoc = this.childDocs[firstIndex];
                         const curDoc = this.sortedDocs.docs[firstIndex];
                         if (firstIndex > 0 && firstIndex < this.childDocs.length) {
                             !e.shiftKey && this.clearSelection();
@@ -224,7 +223,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                     break;
                 }
                 case 'Escape': {
-                    this._selectedCell = undefined;
+                    this.deselectCell();
                 }
             }
         }
@@ -425,6 +424,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._selectedCell = [doc, index];
     };
 
+    @action
+    deselectCell = () => {
+        this._selectedCell = undefined;
+    };
+
     sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc));
 
     setDropIndex = (index: number) => (this._closestDropIndex = index);
@@ -456,7 +460,7 @@ export class CollectionSchemaView extends CollectionSubView() {
             return true;
         }
         const draggedDocs = de.complete.docDragData?.draggedDocuments;
-        if (draggedDocs && super.onInternalDrop(e, de)) {
+        if (draggedDocs && super.onInternalDrop(e, de) && !this.sortField) {
             const pushedDocs = this.childDocs.filter((doc, index) => index >= this._closestDropIndex && !draggedDocs.includes(doc));
             const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs];
             const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc));
@@ -829,6 +833,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get sortedDocs() {
+        trace();
         const field = StrCast(this.layoutDoc.sortField);
         const desc = BoolCast(this.layoutDoc.sortDesc);
         const docs = !field
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index f5a16cec0..9864820a3 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -28,6 +28,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as CollectionSchemaView) : undefined;
     }
 
+    schemaViewFunc = () => this.schemaView;
+
     @computed get schemaDoc() {
         return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
     }
@@ -124,11 +126,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             key={key}
                             Document={this.rootDoc}
                             col={index}
-                            schemaView={this.schemaView}
+                            schemaView={this.schemaViewFunc}
                             fieldKey={key}
                             columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth}
                             isRowActive={this.props.isContentActive}
-                            setColumnValues={(field, value) => this.schemaView?.setColumnValues(field, value) ?? false}
+                            // setColumnValues={(field, value) => this.schemaView?.setColumnValues(field, value) ?? false}
                         />
                     ))}
                 </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 2b61ea261..374b92d72 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -21,11 +21,11 @@ import { SelectionManager } from '../../../util/SelectionManager';
 export interface SchemaTableCellProps {
     Document: Doc;
     col: number;
-    schemaView: CollectionSchemaView | undefined;
+    schemaView: () => CollectionSchemaView | undefined;
     fieldKey: string;
     columnWidth: number;
     isRowActive: () => boolean | undefined;
-    setColumnValues: (field: string, value: string) => boolean;
+    // setColumnValues: (field: string, value: string) => boolean;
 }
 
 @observer
@@ -33,20 +33,34 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     private _editorRef: EditableView | null = null;
 
     @computed get readOnly() {
-        return this.props.schemaView?.fieldInfos[this.props.fieldKey]?.readOnly ?? false;
+        return this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.readOnly ?? false;
     }
 
     @computed get selected() {
-        const selected: [Doc, number] | undefined = this.props.schemaView?._selectedCell;
+        const selected: [Doc, number] | undefined = this.props.schemaView()?._selectedCell;
         return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col;
     }
 
     componentDidUpdate() {
         if (!this.selected) {
             this._editorRef?.setIsFocused(false);
+            document.removeEventListener('keydown', this.onKeyDown);
+        } else if (!this.readOnly) {
+            document.addEventListener('keydown', this.onKeyDown);
         }
     }
 
+    @action
+    onKeyDown = (e: KeyboardEvent) => {
+        e.stopPropagation();
+        if (e.key == 'Enter') {
+            this._editorRef?.setIsFocused(true);
+        }
+        if (e.key == 'Escape') {
+            this.props.schemaView()?.deselectCell();
+        }
+    };
+
     get defaultCellContent() {
         const props: FieldViewProps = {
             Document: this.props.Document,
@@ -81,7 +95,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                     GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
                     SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => {
                         if (shiftDown && enterKey) {
-                            this.props.setColumnValues(this.props.fieldKey, value);
+                            // this.props.setColumnValues(this.props.fieldKey, value);
                         }
                         return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value);
                     }}
@@ -92,7 +106,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     }
 
     get getCellType() {
-        const columnTypeStr = this.props.schemaView?.fieldInfos[this.props.fieldKey]?.fieldType;
+        const columnTypeStr = this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.fieldType;
         if (columnTypeStr) {
             if (columnTypeStr in FInfotoColType) {
                 return FInfotoColType[columnTypeStr];
@@ -125,7 +139,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             <div
                 className="schema-table-cell"
                 onPointerDown={action(e => {
-                    if (!this.selected) this.props.schemaView?.selectCell(this.props.Document, this.props.col);
+                    if (!this.selected) this.props.schemaView()?.selectCell(this.props.Document, this.props.col);
                 })}
                 style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
                 {this.content}
-- 
cgit v1.2.3-70-g09d2


From ba5b687011526188bb024ddf37c254aaf285c06d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Apr 2023 16:45:59 -0400
Subject: performance tuning for schema views to avoid re-rendering each table
 cell when child docs change.

---
 .../collectionLinear/CollectionLinearView.tsx      |  20 ++--
 .../collectionSchema/CollectionSchemaView.tsx      | 105 +++++++++------------
 .../collections/collectionSchema/SchemaRowBox.tsx  |  19 ++--
 .../collectionSchema/SchemaTableCell.tsx           |  48 +++++-----
 4 files changed, 94 insertions(+), 98 deletions(-)

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

diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 97eed7752..efd73a927 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -255,15 +255,17 @@ export class CollectionLinearView extends CollectionSubView() {
                         })}
                     />
 
-                    <div
-                        className="collectionLinearView-content"
-                        style={{
-                            height: this.dimension(),
-                            flexDirection: flexDir as any,
-                            gap: flexGap,
-                        }}>
-                        {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
-                    </div>
+                    {!this.layoutDoc.linearViewIsExpanded ? null : (
+                        <div
+                            className="collectionLinearView-content"
+                            style={{
+                                height: this.dimension(),
+                                flexDirection: flexDir as any,
+                                gap: flexGap,
+                            }}>
+                            {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+                        </div>
+                    )}
                 </div>
             </div>
         );
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 8cd307adf..50a91a60a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,9 +1,9 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx';
+import { action, computed, observable, ObservableMap, observe, reaction, trace, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
-import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, NumListCast, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
@@ -47,6 +47,7 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate',
 
 @observer
 export class CollectionSchemaView extends CollectionSubView() {
+    private _keysDisposer: any;
     private _closestDropIndex: number = 0;
     private _previewRef: HTMLDivElement | null = null;
     private _makeNewColumn: boolean = false;
@@ -58,15 +59,13 @@ export class CollectionSchemaView extends CollectionSubView() {
     public static _rowMenuWidth: number = 60;
     public static _previewDividerWidth: number = 4;
     public static _newNodeInputHeight: number = 30;
+    public fieldInfos = new ObservableMap<string, FInfo>();
 
-    @computed get _selectedDocs() {
-        return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document));
-    }
+    @observable _menuKeys: string[] = [];
     @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
     @observable _colEles: HTMLDivElement[] = [];
     @observable _displayColumnWidths: number[] | undefined;
     @observable _columnMenuIndex: number | undefined;
-    @observable _fieldInfos: [string, FInfo][] = [];
     @observable _newFieldWarning: string = '';
     @observable _makeNewField: boolean = false;
     @observable _newFieldDefault: any = 0;
@@ -76,34 +75,12 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _filterSearchValue: string = '';
     @observable _selectedCell: [Doc, number] | undefined;
 
-    get fieldInfos() {
-        const docs = this.childDocs;
-        const keys: { [key: string]: boolean } = {};
-        // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
-        //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
-        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
-        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
-        //  is displayed (unlikely) it won't show up until something else changes.
-        //TODO Types
-        untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
-
-        let computedKeys: { [key: string]: FInfo } = {};
-
-        Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => {
-            computedKeys[pair[0]] = pair[1];
-        });
-
-        Object.keys(keys).forEach((key: string) => {
-            if (!(key in computedKeys)) {
-                computedKeys[key] = new FInfo('');
-            }
-        });
-
-        return computedKeys;
+    @computed get _selectedDocs() {
+        return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.rootDoc));
     }
 
-    get documentKeys() {
-        return Object.keys(this.fieldInfos);
+    @computed get documentKeys() {
+        return Array.from(this.fieldInfos.keys());
     }
 
     @computed get previewWidth() {
@@ -119,20 +96,15 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get storedColumnWidths() {
-        let widths = Cast(
+        const widths = NumListCast(
             this.layoutDoc.columnWidths,
-            listSpec('number'),
             this.columnKeys.map(() => (this.tableWidth - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
         );
 
         const totalWidth = widths.reduce((sum, width) => sum + width, 0);
         if (totalWidth !== this.tableWidth - CollectionSchemaView._rowMenuWidth) {
-            widths = widths.map(w => {
-                const proportion = w / totalWidth;
-                return proportion * (this.tableWidth - CollectionSchemaView._rowMenuWidth);
-            });
+            return widths.map(w => (w / totalWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth));
         }
-
         return widths;
     }
 
@@ -148,19 +120,37 @@ export class CollectionSchemaView extends CollectionSubView() {
         return BoolCast(this.layoutDoc.sortDesc);
     }
 
-    rowIndex(doc: Doc) {
-        return this.sortedDocs.docs.indexOf(doc);
-    }
-
+    @action
     componentDidMount() {
         this.props.setContentView?.(this);
         document.addEventListener('keydown', this.onKeyDown);
+
+        Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
+        this._keysDisposer = observe(
+            this.rootDoc[this.fieldKey ?? 'data'] as List<Doc>,
+            change => {
+                switch (change.type as any) {
+                    case 'splice':
+                        // prettier-ignore
+                        (change as any).added.forEach((doc: Doc) => // for each document added 
+                            Doc.GetAllPrototypes(doc).forEach(proto => // for all of its prototypes (and itself)
+                                Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them
+                                    !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo(''))))));
+                        break;
+                    case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
+                }
+            },
+            true
+        );
     }
 
     componentWillUnmount() {
+        this._keysDisposer?.();
         document.removeEventListener('keydown', this.onKeyDown);
     }
 
+    rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc);
+
     @action
     onKeyDown = (e: KeyboardEvent) => {
         if (this._selectedDocs.length > 0) {
@@ -554,8 +544,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     onSearchKeyDown = (e: React.KeyboardEvent) => {
         switch (e.key) {
             case 'Enter':
-                const menuKeys = Object.keys(this._fieldInfos);
-                menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(menuKeys[0]) : action(() => (this._makeNewField = true))();
+                this._menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuKeys[0]) : action(() => (this._makeNewField = true))();
                 break;
             case 'Escape':
                 this.closeColumnMenu();
@@ -584,7 +573,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._makeNewColumn = false;
         this._columnMenuIndex = index;
         this._menuValue = '';
-        this._fieldInfos = Object.entries(this.fieldInfos);
+        this._menuKeys = this.documentKeys;
         this._makeNewField = false;
         this._newFieldWarning = '';
         this._makeNewField = false;
@@ -630,7 +619,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @action
     updateKeySearch = (e: React.ChangeEvent<HTMLInputElement>) => {
         this._menuValue = e.target.value;
-        this._fieldInfos = Object.entries(this.fieldInfos).filter(value => value[0].toLowerCase().includes(this._menuValue.toLowerCase()));
+        this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
     };
 
     getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
@@ -732,7 +721,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                             { passive: false }
                         )
                     }>
-                    {this._fieldInfos.map(([key, info]) => (
+                    {this._menuKeys.map(key => (
                         <div
                             className="schema-search-result"
                             onPointerDown={e => {
@@ -742,13 +731,13 @@ export class CollectionSchemaView extends CollectionSubView() {
                             <p>
                                 <span className="schema-search-result-key">
                                     {key}
-                                    {info.fieldType ? ', ' : ''}
+                                    {this.fieldInfos.get(key)!.fieldType ? ', ' : ''}
                                 </span>
-                                <span className="schema-search-result-type" style={{ color: info.readOnly ? 'red' : 'inherit' }}>
-                                    {info.fieldType}
+                                <span className="schema-search-result-type" style={{ color: this.fieldInfos.get(key)!.readOnly ? 'red' : 'inherit' }}>
+                                    {this.fieldInfos.get(key)!.fieldType}
                                 </span>
                             </p>
-                            <p className="schema-search-result-desc">{info.description}</p>
+                            <p className="schema-search-result-desc">{this.fieldInfos.get(key)!.description}</p>
                         </div>
                     ))}
                 </div>
@@ -888,13 +877,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                     </div>
                     {this._columnMenuIndex !== undefined && this.renderColumnMenu}
                     {this._filterColumnIndex !== undefined && this.renderFilterMenu}
-                    <CollectionSchemaViewDocs
-                        schema={this}
-                        childDocs={this.sortedDocsFunc}
-                        setRef={(ref: HTMLDivElement | null) => {
-                            this._tableContentRef = ref;
-                        }}
-                    />
+                    <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} height={CollectionSchemaView._newNodeInputHeight} />
                 </div>
                 {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
@@ -923,7 +906,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 moveDocument={this.props.moveDocument}
                                 addDocument={this.addRow}
                                 removeDocument={this.props.removeDocument}
-                                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                                whenChildContentsActiveChanged={returnFalse}
                                 addDocTab={this.props.addDocTab}
                                 pinToPres={this.props.pinToPres}
                                 bringToFront={returnFalse}
@@ -978,7 +961,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 bringToFront={emptyFunction}
                                 isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive}
                                 isContentActive={emptyFunction}
-                                whenChildContentsActiveChanged={active => this.props.schema.props.whenChildContentsActiveChanged(active)}
+                                whenChildContentsActiveChanged={this.props.schema.props.whenChildContentsActiveChanged}
                                 hideDecorations={true}
                                 hideTitle={true}
                                 hideDocumentButtonBar={true}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 9864820a3..9772ce118 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -12,6 +12,8 @@ import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
+import { computedFn } from 'mobx-utils';
+import { Doc } from '../../../../fields/Doc';
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -24,12 +26,9 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     bounds = () => this._ref?.getBoundingClientRect();
 
     @computed get schemaView() {
-        const vpath = this.props.docViewPath();
-        return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as CollectionSchemaView) : undefined;
+        return this.props.DocumentView?.().props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView;
     }
 
-    schemaViewFunc = () => this.schemaView;
-
     @computed get schemaDoc() {
         return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
     }
@@ -86,6 +85,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         document.removeEventListener('pointermove', this.onPointerMove);
     };
 
+    getFinfo = computedFn((fieldKey: string) => this.schemaView?.fieldInfos.get(fieldKey));
+    selectCell = (doc: Doc, col: number) => this.schemaView?.selectCell(doc, col);
+    deselectCell = () => this.schemaView?.deselectCell();
+    selectedCell = () => this.schemaView?._selectedCell;
+    setColumnValues = (field: any, value: any) => this.schemaView?.setColumnValues(field, value) ?? false;
     render() {
         return (
             <div
@@ -126,11 +130,14 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             key={key}
                             Document={this.rootDoc}
                             col={index}
-                            schemaView={this.schemaViewFunc}
                             fieldKey={key}
                             columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth}
                             isRowActive={this.props.isContentActive}
-                            // setColumnValues={(field, value) => this.schemaView?.setColumnValues(field, value) ?? false}
+                            getFinfo={this.getFinfo}
+                            selectCell={this.selectCell}
+                            deselectCell={this.deselectCell}
+                            selectedCell={this.selectedCell}
+                            setColumnValues={this.setColumnValues}
                         />
                     ))}
                 </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 374b92d72..686b21283 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,31 +1,34 @@
 import React = require('react');
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
-import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils';
+import { extname } from 'path';
+import DatePicker from 'react-datepicker';
+import { DateField } from '../../../../fields/DateField';
+import { Doc, DocListCast, Field } from '../../../../fields/Doc';
+import { Cast, DateCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
 import { Transform } from '../../../util/Transform';
 import { EditableView } from '../../EditableView';
+import { Colors } from '../../global/globalEnums';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { DefaultStyleProvider } from '../../StyleProvider';
 import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
-import { action, computed, observable } from 'mobx';
-import { extname } from 'path';
-import { Cast, DateCast, StrCast } from '../../../../fields/Types';
-import { ImageField } from '../../../../fields/URLField';
-import { DateField } from '../../../../fields/DateField';
-import DatePicker from 'react-datepicker';
-import { Colors } from '../../global/globalEnums';
-import { SelectionManager } from '../../../util/SelectionManager';
+import { FInfo } from '../../../documents/Documents';
 
 export interface SchemaTableCellProps {
     Document: Doc;
     col: number;
-    schemaView: () => CollectionSchemaView | undefined;
+    deselectCell: () => void;
+    selectCell: (doc: Doc, col: number) => void;
+    selectedCell: () => [Doc, number] | undefined;
     fieldKey: string;
     columnWidth: number;
     isRowActive: () => boolean | undefined;
-    // setColumnValues: (field: string, value: string) => boolean;
+    getFinfo: (fieldKey: string) => FInfo | undefined;
+    setColumnValues: (field: string, value: string) => boolean;
 }
 
 @observer
@@ -33,11 +36,11 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     private _editorRef: EditableView | null = null;
 
     @computed get readOnly() {
-        return this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.readOnly ?? false;
+        return this.props.getFinfo(this.props.fieldKey)?.readOnly ?? false;
     }
 
     @computed get selected() {
-        const selected: [Doc, number] | undefined = this.props.schemaView()?._selectedCell;
+        const selected: [Doc, number] | undefined = this.props.selectedCell();
         return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col;
     }
 
@@ -57,11 +60,12 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             this._editorRef?.setIsFocused(true);
         }
         if (e.key == 'Escape') {
-            this.props.schemaView()?.deselectCell();
+            this.props.deselectCell();
         }
     };
-
-    get defaultCellContent() {
+    colWidthFunc = () => this.props.columnWidth;
+    colRowHeightFunc = () => CollectionSchemaView._rowHeight;
+    @computed get defaultCellContent() {
         const props: FieldViewProps = {
             Document: this.props.Document,
             docFilters: returnEmptyFilter,
@@ -81,8 +85,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             whenChildContentsActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
-            PanelWidth: () => this.props.columnWidth,
-            PanelHeight: () => CollectionSchemaView._rowHeight,
+            PanelWidth: this.colWidthFunc,
+            PanelHeight: this.colRowHeightFunc,
             addDocTab: returnFalse,
             pinToPres: returnZero,
         };
@@ -95,7 +99,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                     GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
                     SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => {
                         if (shiftDown && enterKey) {
-                            // this.props.setColumnValues(this.props.fieldKey, value);
+                            this.props.setColumnValues(this.props.fieldKey, value);
                         }
                         return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value);
                     }}
@@ -106,7 +110,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     }
 
     get getCellType() {
-        const columnTypeStr = this.props.schemaView()?.fieldInfos[this.props.fieldKey]?.fieldType;
+        const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType;
         if (columnTypeStr) {
             if (columnTypeStr in FInfotoColType) {
                 return FInfotoColType[columnTypeStr];
@@ -139,7 +143,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             <div
                 className="schema-table-cell"
                 onPointerDown={action(e => {
-                    if (!this.selected) this.props.schemaView()?.selectCell(this.props.Document, this.props.col);
+                    if (!this.selected) this.props.selectCell(this.props.Document, this.props.col);
                 })}
                 style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
                 {this.content}
-- 
cgit v1.2.3-70-g09d2


From 223dde9f35408c229a4da583083d10cbf81fc264 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Apr 2023 22:23:42 -0400
Subject: fixed some undo issues with schema fill down.  added bool cell view. 
 added UI for showing/setting values on layout vs data docs.

---
 .../collections/collectionSchema/SchemaRowBox.tsx  |   3 +-
 .../collectionSchema/SchemaTableCell.tsx           | 156 ++++++++++++---------
 src/client/views/nodes/KeyValueBox.tsx             |   2 -
 3 files changed, 89 insertions(+), 72 deletions(-)

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

diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 9772ce118..ca9e0bda0 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -90,6 +90,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     deselectCell = () => this.schemaView?.deselectCell();
     selectedCell = () => this.schemaView?._selectedCell;
     setColumnValues = (field: any, value: any) => this.schemaView?.setColumnValues(field, value) ?? false;
+    columnWidth = computedFn((index: number) => () => this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth);
     render() {
         return (
             <div
@@ -131,7 +132,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             Document={this.rootDoc}
                             col={index}
                             fieldKey={key}
-                            columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth}
+                            columnWidth={this.columnWidth(index)}
                             isRowActive={this.props.isContentActive}
                             getFinfo={this.getFinfo}
                             selectCell={this.selectCell}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 4f4986b90..1fa4312e1 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -5,10 +5,13 @@ import { extname } from 'path';
 import DatePicker from 'react-datepicker';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field } from '../../../../fields/Doc';
-import { Cast, DateCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DateCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
+import { FInfo } from '../../../documents/Documents';
+import { dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
 import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
@@ -16,7 +19,6 @@ import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { DefaultStyleProvider } from '../../StyleProvider';
 import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
-import { FInfo } from '../../../documents/Documents';
 
 export interface SchemaTableCellProps {
     Document: Doc;
@@ -25,7 +27,7 @@ export interface SchemaTableCellProps {
     selectCell: (doc: Doc, col: number) => void;
     selectedCell: () => [Doc, number] | undefined;
     fieldKey: string;
-    columnWidth: number;
+    columnWidth: () => number;
     isRowActive: () => boolean | undefined;
     getFinfo: (fieldKey: string) => FInfo | undefined;
     setColumnValues: (field: string, value: string) => boolean;
@@ -33,95 +35,78 @@ export interface SchemaTableCellProps {
 
 @observer
 export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
-    private _editorRef: EditableView | null = null;
-
-    @computed get readOnly() {
-        return this.props.getFinfo(this.props.fieldKey)?.readOnly ?? false;
-    }
-
-    @computed get selected() {
-        const selected: [Doc, number] | undefined = this.props.selectedCell();
-        return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col;
+    public static colRowHeightFunc() {
+        return CollectionSchemaView._rowHeight;
     }
-
-    componentDidUpdate() {
-        if (!this.selected) {
-            this._editorRef?.setIsFocused(false);
-            document.removeEventListener('keydown', this.onKeyDown);
-        } else if (!this.readOnly) {
-            document.addEventListener('keydown', this.onKeyDown);
-        }
-    }
-
-    @action
-    onKeyDown = (e: KeyboardEvent) => {
-        e.stopPropagation();
-        if (e.key == 'Enter') {
-            this._editorRef?.setIsFocused(true);
-        }
-        if (e.key == 'Escape') {
-            this.props.deselectCell();
+    public static renderProps(props: SchemaTableCellProps) {
+        const { Document, fieldKey, getFinfo, columnWidth, isRowActive } = props;
+        let protoCount = 0;
+        let doc: Doc | undefined = Document;
+        while (doc) {
+            if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) {
+                break;
+            }
+            protoCount++;
+            doc = doc.proto;
         }
-    };
-    colWidthFunc = () => this.props.columnWidth;
-    colRowHeightFunc = () => CollectionSchemaView._rowHeight;
-    @computed get defaultCellContent() {
-        const props: FieldViewProps = {
-            Document: this.props.Document,
+        const parenCount = Math.max(0, protoCount - 1);
+        const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue';
+        const textDecoration = color !== 'black' && parenCount ? 'underline' : '';
+        const fieldProps: FieldViewProps = {
             docFilters: returnEmptyFilter,
             docRangeFilters: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
             docViewPath: returnEmptyDoclist,
-            fieldKey: this.props.fieldKey,
             rootSelected: returnFalse,
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'alias',
+            dropAction: 'alias' as dropActionType,
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
             whenChildContentsActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
-            PanelWidth: this.colWidthFunc,
-            PanelHeight: this.colRowHeightFunc,
             addDocTab: returnFalse,
             pinToPres: returnZero,
+            Document,
+            fieldKey,
+            PanelWidth: columnWidth,
+            PanelHeight: SchemaTableCell.colRowHeightFunc,
         };
-        let protoCount = 0;
-        let doc: Doc | undefined = this.props.Document;
-        while (doc) {
-            if (Object.keys(doc).includes(this.props.fieldKey.replace(/^_/, ''))) {
-                break;
-            }
-            protoCount++;
-            doc = doc.proto;
-        }
-        const parenCount = Math.max(0, protoCount - 1);
-        const color = protoCount === 0 ? 'black' : 'blue';
+        const readOnly = getFinfo(fieldKey)?.readOnly ?? false;
+        const cursor = !readOnly ? 'text' : 'default';
+        const pointerEvents: 'all' | 'none' = !readOnly && isRowActive() ? 'all' : 'none';
+        return { color, textDecoration, fieldProps, cursor, pointerEvents };
+    }
+
+    @computed get selected() {
+        const selected: [Doc, number] | undefined = this.props.selectedCell();
+        return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col;
+    }
+
+    @computed get defaultCellContent() {
+        const { color, textDecoration, fieldProps } = SchemaTableCell.renderProps(this.props);
 
         return (
             <div
                 className="schemacell-edit-wrapper"
                 style={{
                     color,
-                    textDecoration: parenCount ? 'underline' : undefined,
-                    cursor: !this.readOnly ? 'text' : 'default',
-                    pointerEvents: !this.readOnly && this.props.isRowActive() ? 'all' : 'none',
+                    textDecoration,
                 }}>
                 <EditableView
-                    ref={ref => (this._editorRef = ref)}
-                    contents={<FieldView {...props} />}
+                    contents={<FieldView {...fieldProps} />}
+                    editing={this.selected ? undefined : false}
                     GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
-                    SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => {
+                    SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
                         if (shiftDown && enterKey) {
                             this.props.setColumnValues(this.props.fieldKey, value);
                         }
                         return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value);
-                    }}
-                    editing={this.selected ? undefined : false}
+                    })}
                 />
             </div>
         );
@@ -146,13 +131,12 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
 
     get content() {
         const cellType: ColumnType = this.getCellType;
+        // prettier-ignore
         switch (cellType) {
-            case ColumnType.Image:
-                return <SchemaImageCell {...this.props} />;
-            case ColumnType.Date:
-            // return <SchemaDateCell {...this.props} />;
-            default:
-                return this.defaultCellContent;
+            case ColumnType.Image: return <SchemaImageCell {...this.props} />;
+            case ColumnType.Boolean: return <SchemaBoolCell {...this.props} />;
+            case ColumnType.Date: // return <SchemaDateCell {...this.props} />;
+            default:  return this.defaultCellContent;
         }
     }
 
@@ -160,10 +144,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
         return (
             <div
                 className="schema-table-cell"
-                onPointerDown={action(e => {
-                    if (!this.selected) this.props.selectCell(this.props.Document, this.props.col);
-                })}
-                style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
+                onPointerDown={action(e => !this.selected && this.props.selectCell(this.props.Document, this.props.col))}
+                style={{ width: this.props.columnWidth(), border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
                 {this.content}
             </div>
         );
@@ -257,3 +239,39 @@ export class SchemaDateCell extends React.Component<SchemaTableCellProps> {
         return <DatePicker dateFormat={'Pp'} selected={this.date.date} onChange={(date: any) => this.handleChange(date)} />;
     }
 }
+@observer
+export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
+    @computed get selected() {
+        const selected: [Doc, number] | undefined = this.props.selectedCell();
+        return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col;
+    }
+    render() {
+        const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props);
+        return (
+            <div className="schemaBoolCell" style={{ display: 'flex', color, textDecoration, cursor, pointerEvents }}>
+                <input
+                    style={{ marginRight: 4 }}
+                    type="checkbox"
+                    checked={BoolCast(this.props.Document[this.props.fieldKey])}
+                    onChange={undoBatch((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
+                        if ((value?.nativeEvent as any).shiftKey) {
+                            this.props.setColumnValues(this.props.fieldKey, (color === 'black' ? '=' : '') + value?.target?.checked.toString());
+                        }
+                        KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
+                    })}
+                />
+                <EditableView
+                    contents={<FieldView {...fieldProps} />}
+                    editing={this.selected ? undefined : false}
+                    GetValue={() => (color === 'black' ? '=' : '') + Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
+                    SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
+                        if (shiftDown && enterKey) {
+                            this.props.setColumnValues(this.props.fieldKey, value);
+                        }
+                        return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+                    })}
+                />
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 57018fb93..11220c300 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -20,14 +20,12 @@ import { ImageBox } from './ImageBox';
 import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import React = require('react');
-import e = require('express');
 
 export type KVPScript = {
     script: CompiledScript;
     type: 'computed' | 'script' | false;
     onDelegate: boolean;
 };
-
 @observer
 export class KeyValueBox extends React.Component<FieldViewProps> {
     public static LayoutString() {
-- 
cgit v1.2.3-70-g09d2


From dca61505ba138eef3819b16b760ec81becf9329e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 13 May 2023 09:55:48 -0400
Subject: changed EditableViews to support oneline and multiline.  Also added
 transformer UI to allow documents to be entered. changed transformer to write
 doc id's, not variables..  made schema view support oneline and fixed bug
 with docdecoration hader occluding things invisibly.   updated web pages to
 be zoomable and for its anchors to update web page and scroll location
 properly.  made autolinkanchor directly go to target on click.

---
 package-lock.json                                  |  39 +++
 src/client/documents/Documents.ts                  |   4 +-
 src/client/util/CurrentUserUtils.ts                |   5 +-
 src/client/util/DocumentManager.ts                 |  24 +-
 src/client/util/Scripting.ts                       |  30 +-
 src/client/views/DocumentDecorations.tsx           |   8 +-
 src/client/views/EditableView.scss                 |  11 +-
 src/client/views/EditableView.tsx                  |  98 ++++--
 src/client/views/MarqueeAnnotator.tsx              |   3 +-
 src/client/views/ScriptingRepl.scss                |   5 +-
 src/client/views/ScriptingRepl.tsx                 | 113 ++++---
 .../views/collections/CollectionStackingView.scss  |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  18 +-
 .../collectionSchema/CollectionSchemaView.scss     |   4 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  23 +-
 .../collectionSchema/SchemaColumnHeader.tsx        |   1 +
 .../collections/collectionSchema/SchemaRowBox.tsx  |  10 +-
 .../collectionSchema/SchemaTableCell.tsx           |  44 ++-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   3 +-
 src/client/views/nodes/DocumentIcon.tsx            |  56 ++--
 src/client/views/nodes/DocumentView.tsx            |   4 +-
 src/client/views/nodes/KeyValueBox.tsx             |   6 +-
 src/client/views/nodes/KeyValuePair.scss           |  64 ++--
 src/client/views/nodes/KeyValuePair.tsx            |   8 +-
 src/client/views/nodes/WebBox.tsx                  | 359 ++++++++++-----------
 src/client/views/nodes/button/FontIconBox.tsx      |  13 +-
 .../views/nodes/formattedText/DashFieldView.tsx    | 150 ++-------
 .../views/nodes/formattedText/FormattedTextBox.tsx |   3 +-
 src/client/views/nodes/formattedText/marks_rts.ts  |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  18 +-
 src/client/views/pdf/PDFViewer.tsx                 |   3 +-
 src/fields/Doc.ts                                  |   2 +-
 32 files changed, 605 insertions(+), 532 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 196906718..111d1838a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5597,6 +5597,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3": {
             "version": "7.8.4",
             "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
@@ -6977,6 +6987,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6988,6 +7020,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -22660,6 +22693,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5a7894c08..bb12ce568 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1054,8 +1054,8 @@ export namespace Docs {
             return inst;
         }
 
-        export function WebanchorDocument(url?: string, options: DocumentOptions = {}, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), url, options, id);
+        export function WebanchorDocument(options: DocumentOptions = {}, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), undefined, options, id);
         }
 
         export function CollectionAnchorDocument(options: DocumentOptions = {}, id?: string) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 80a5f0993..fbfe306a4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -669,7 +669,10 @@ export class CurrentUserUtils {
     }
 
     static schemaTools():Button[] {
-        return [{ title: "Show preview", toolTip: "Show selection preview", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{ return toggleSchemaPreview(_readOnly_); }'}, }];
+        return [
+            {title: "Show preview", toolTip: "Show selection preview", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{ return toggleSchemaPreview(_readOnly_); }'} },
+            {title: "Single Lines", toolTip: "Single Line Rows",       btnType: ButtonType.ToggleButton, buttonText: "Single Line",  icon: "eye", scripts:{ onClick: '{ return toggleSingleLineSchema(_readOnly_); }'} },
+        ];
     }
 
     static webTools() {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 3a192f712..695b003a6 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,4 +1,4 @@
-import { action, observable, ObservableSet } from 'mobx';
+import { action, computed, observable, ObservableSet } from 'mobx';
 import { AnimationSym, Doc, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { listSpec } from '../../fields/Schema';
@@ -10,6 +10,7 @@ import { TabDocView } from '../views/collections/TabDocView';
 import { LightboxView } from '../views/LightboxView';
 import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
 import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
+import { KeyValueBox } from '../views/nodes/KeyValueBox';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
 import { PresBox } from '../views/nodes/trails';
 import { ScriptingGlobals } from './ScriptingGlobals';
@@ -18,10 +19,13 @@ const { Howl } = require('howler');
 
 export class DocumentManager {
     //global holds all of the nodes (regardless of which collection they're in)
-    @observable public DocumentViews = new Set<DocumentView>();
+    @observable _documentViews = new Set<DocumentView>();
     @observable public LinkAnchorBoxViews: DocumentView[] = [];
     @observable public RecordingEvent = 0;
     @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = [];
+    @computed public get DocumentViews() {
+        return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox));
+    }
 
     private static _instance: DocumentManager;
     public static get Instance(): DocumentManager {
@@ -78,7 +82,7 @@ export class DocumentManager {
             // this.LinkedDocumentViews.forEach(view => console.log("  LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
             //     view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString));
         } else {
-            this.DocumentViews.add(view);
+            this._documentViews.add(view);
         }
         this.callAddViewFuncs(view);
     };
@@ -96,7 +100,7 @@ export class DocumentManager {
             const index = this.LinkAnchorBoxViews.indexOf(view);
             this.LinkAnchorBoxViews.splice(index, 1);
         } else {
-            this.DocumentViews.delete(view);
+            this._documentViews.delete(view);
         }
         SelectionManager.DeselectView(view);
     });
@@ -104,13 +108,13 @@ export class DocumentManager {
     //gets all views
     public getDocumentViewsById(id: string) {
         const toReturn: DocumentView[] = [];
-        Array.from(DocumentManager.Instance.DocumentViews).map(view => {
+        DocumentManager.Instance.DocumentViews.forEach(view => {
             if (view.rootDoc[Id] === id) {
                 toReturn.push(view);
             }
         });
         if (toReturn.length === 0) {
-            Array.from(DocumentManager.Instance.DocumentViews).map(view => {
+            DocumentManager.Instance.DocumentViews.forEach(view => {
                 const doc = view.rootDoc.proto;
                 if (doc && doc[Id] && doc[Id] === id) {
                     toReturn.push(view);
@@ -133,7 +137,7 @@ export class DocumentManager {
             //         ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFind.annotationOn)?.data as any)?.url?.href)
             // )?.rootDoc ??
             toFind;
-        const docViewArray = Array.from(DocumentManager.Instance.DocumentViews);
+        const docViewArray = DocumentManager.Instance.DocumentViews;
         const passes = !doc ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined];
         return passes.reduce(
             (pass, toReturn) =>
@@ -146,7 +150,7 @@ export class DocumentManager {
 
     public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
         const views: DocumentView[] = [];
-        Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
+        DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined);
     };
     public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
@@ -164,8 +168,8 @@ export class DocumentManager {
             toFindIn;
 
         const toReturn: DocumentView[] = [];
-        const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
-        const lightViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
+        const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
+        const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
 
         // heuristic to return the "best" documents first:
         //   choose a document in the lightbox first
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index f17a98616..70c2e3842 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -7,8 +7,6 @@
 import * as typescriptlib from '!!raw-loader!./type_decls.d';
 import * as ts from 'typescript';
 import { Doc, Field } from '../../fields/Doc';
-import { ToScriptString } from '../../fields/FieldSymbols';
-import { ObjectField } from '../../fields/ObjectField';
 import { RefField } from '../../fields/RefField';
 import { ScriptField } from '../../fields/ScriptField';
 import { scriptingGlobals, ScriptingGlobals } from './ScriptingGlobals';
@@ -60,7 +58,14 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
     // let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript];
     // let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)];
     // let params: any[] = [Docs, ...fieldTypes];
-    const compiledFunction = new Function(...paramNames, `return ${script}`);
+    const compiledFunction = (() => {
+        try {
+            return new Function(...paramNames, `return ${script}`);
+        } catch {
+            return undefined;
+        }
+    })();
+    if (!compiledFunction) return { compiled: false, errors };
     const { capturedVariables = {} } = options;
     const run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => {
         const argsArray: any[] = [];
@@ -155,7 +160,7 @@ export type Traverser = (node: ts.Node, indentation: string) => boolean | void;
 export type TraverserParam = Traverser | { onEnter: Traverser; onLeave: Traverser };
 export type Transformer = {
     transformer: ts.TransformerFactory<ts.SourceFile>;
-    getVars?: () => { capturedVariables: { [name: string]: Field } };
+    getVars?: () => { [name: string]: Field };
 };
 export interface ScriptOptions {
     requiredType?: string; // does function required a typed return value
@@ -205,16 +210,17 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
     if (options.transformer) {
         const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true);
         const result = ts.transform(sourceFile, [options.transformer.transformer]);
-        if (options.transformer.getVars) {
-            const newCaptures = options.transformer.getVars();
+        const newCaptures = options.transformer.getVars?.();
+        if (Object.keys(newCaptures ?? {}).length) {
             // tslint:disable-next-line: prefer-object-spread
-            options.capturedVariables = Object.assign(capturedVariables, newCaptures.capturedVariables) as any;
+            //options.capturedVariables = Object.assign(capturedVariables, newCaptures!) as any;
+
+            const transformed = result.transformed;
+            const printer = ts.createPrinter({
+                newLine: ts.NewLineKind.LineFeed,
+            });
+            script = printer.printFile(transformed[0]);
         }
-        const transformed = result.transformed;
-        const printer = ts.createPrinter({
-            newLine: ts.NewLineKind.LineFeed,
-        });
-        script = printer.printFile(transformed[0]);
         result.dispose();
     }
     const paramNames: string[] = [];
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 85d36dbf8..26b64bfdd 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -867,11 +867,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 width: bounds.r - bounds.x + this._resizeBorderWidth + 'px',
                                 height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px',
                             }}>
-                            <div className="documentDecorations-topbar" onPointerDown={this.onContainerDown}>
-                                {hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
-                                {hideResizers || hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
+                            <div className="documentDecorations-topbar" style={{ display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined }} onPointerDown={this.onContainerDown}>
+                                {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
+                                {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
-                                {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
+                                {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
                                 <>
diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss
index ed7ec9dc1..0955ba8ff 100644
--- a/src/client/views/EditableView.scss
+++ b/src/client/views/EditableView.scss
@@ -10,12 +10,14 @@
 
 .editableView-container-editing-oneLine {
     width: 100%;
+    height: max-content;
 
     span {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        display: block;
+        p {
+            white-space: pre;
+            overflow: hidden;
+            text-overflow: ellipsis;
+        }
     }
 
     input {
@@ -33,4 +35,3 @@
     border: none;
     outline: none;
 }
- 
\ No newline at end of file
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index d1311a60a..6b4132814 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,9 +1,11 @@
 import React = require('react');
-import { action, observable } from 'mobx';
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as Autosuggest from 'react-autosuggest';
 import { ObjectField } from '../../fields/ObjectField';
 import './EditableView.scss';
+import { DocumentIconContainer } from './nodes/DocumentIcon';
+import { OverlayView } from './OverlayView';
 
 export interface EditableProps {
     /**
@@ -37,7 +39,8 @@ export interface EditableProps {
         onChange: (e: React.ChangeEvent, { newValue }: { newValue: string }) => void;
         autosuggestProps: Autosuggest.AutosuggestProps<string, any>;
     };
-    oneLine?: boolean;
+    oneLine?: boolean; // whether to display the editable view as a single input line or as a textarea
+    allowCRs?: boolean; // can carriage returns be entered
     editing?: boolean;
     isEditingCallback?: (isEditing: boolean) => void;
     menuCallback?: (x: number, y: number) => void;
@@ -45,6 +48,7 @@ export interface EditableProps {
     showMenuOnLoad?: boolean;
     background?: string | undefined;
     placeholder?: string;
+    wrap?: string; // nowrap, pre-wrap, etc
 }
 
 /**
@@ -55,7 +59,9 @@ export interface EditableProps {
 @observer
 export class EditableView extends React.Component<EditableProps> {
     private _ref = React.createRef<HTMLDivElement>();
-    private _inputref = React.createRef<HTMLInputElement>();
+    private _inputref: HTMLInputElement | HTMLTextAreaElement | null = null;
+    _overlayDisposer?: () => void;
+    _editingDisposer?: IReactionDisposer;
     @observable _editing: boolean = false;
 
     constructor(props: EditableProps) {
@@ -63,21 +69,53 @@ export class EditableView extends React.Component<EditableProps> {
         this._editing = this.props.editing ? true : false;
     }
 
+    componentDidMount(): void {
+        this._editingDisposer = reaction(
+            () => this._editing,
+            editing => {
+                if (editing) {
+                    setTimeout(() => {
+                        if (this._inputref?.value.startsWith('=') || this._inputref?.value.startsWith(':=')) {
+                            this._overlayDisposer?.();
+                            this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+                        }
+                    });
+                } else {
+                    this._overlayDisposer?.();
+                    this._overlayDisposer = undefined;
+                }
+            },
+            { fireImmediately: true }
+        );
+    }
+
     @action
     componentDidUpdate() {
         if (this._editing && this.props.editing === false) {
-            this._inputref.current?.value && this.finalizeEdit(this._inputref.current.value, false, true, false);
+            this._inputref?.value && this.finalizeEdit(this._inputref.value, false, true, false);
         } else if (this.props.editing !== undefined) {
             this._editing = this.props.editing;
         }
     }
 
     componentWillUnmount() {
-        this._inputref.current?.value && this.finalizeEdit(this._inputref.current.value, false, true, false);
+        this._overlayDisposer?.();
+        this._editingDisposer?.();
+        this._inputref?.value && this.finalizeEdit(this._inputref.value, false, true, false);
     }
 
+    onChange = (e: React.ChangeEvent) => {
+        const targVal = (e.target as any).value;
+        if (!(targVal.startsWith(':=') || targVal.startsWith('='))) {
+            this._overlayDisposer?.();
+            this._overlayDisposer = undefined;
+        } else if (!this._overlayDisposer) {
+            this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+        }
+    };
+
     @action
-    onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+    onKeyDown = (e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
         switch (e.key) {
             case 'Tab':
                 e.stopPropagation();
@@ -89,13 +127,15 @@ export class EditableView extends React.Component<EditableProps> {
                 if (!e.currentTarget.value) this.props.OnEmpty?.();
                 break;
             case 'Enter':
-                e.stopPropagation();
-                if (!e.ctrlKey) {
-                    this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true);
-                } else if (this.props.OnFillDown) {
-                    this.props.OnFillDown(e.currentTarget.value);
-                    this._editing = false;
-                    this.props.isEditingCallback?.(false);
+                if (this.props.allowCRs !== true) {
+                    e.stopPropagation();
+                    if (!e.ctrlKey) {
+                        this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true);
+                    } else if (this.props.OnFillDown) {
+                        this.props.OnFillDown(e.currentTarget.value);
+                        this._editing = false;
+                        this.props.isEditingCallback?.(false);
+                    }
                 }
                 break;
             case 'Escape':
@@ -103,9 +143,6 @@ export class EditableView extends React.Component<EditableProps> {
                 this._editing = false;
                 this.props.isEditingCallback?.(false);
                 break;
-            case ':':
-                this.props.menuCallback?.(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
-                break;
             case 'ArrowUp':
             case 'ArrowDown':
             case 'ArrowLeft':
@@ -117,6 +154,12 @@ export class EditableView extends React.Component<EditableProps> {
             case 'Meta':
             case 'Control':
                 break;
+            case ':':
+                if (this.props.menuCallback) {
+                    this.props.menuCallback(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
+                    break;
+                }
+
             default:
                 if (this.props.textCallback?.(e.key)) {
                     this._editing = false;
@@ -186,15 +229,32 @@ export class EditableView extends React.Component<EditableProps> {
                     onChange: this.props.autosuggestProps.onChange,
                 }}
             />
-        ) : (
+        ) : this.props.oneLine !== false && this.props.GetValue()?.toString().indexOf('\n') === -1 ? (
             <input
                 className="editableView-input"
-                ref={this._inputref}
+                ref={r => (this._inputref = r)}
                 style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minWidth: 20, background: this.props.background }}
                 placeholder={this.props.placeholder}
                 onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
                 defaultValue={this.props.GetValue()}
                 autoFocus={true}
+                onChange={this.onChange}
+                onKeyDown={this.onKeyDown}
+                onKeyPress={this.stopPropagation}
+                onPointerDown={this.stopPropagation}
+                onClick={this.stopPropagation}
+                onPointerUp={this.stopPropagation}
+            />
+        ) : (
+            <textarea
+                className="editableView-input"
+                ref={r => (this._inputref = r)}
+                style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minHeight: `min(100%, ${(this.props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20, background: this.props.background }}
+                placeholder={this.props.placeholder}
+                onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
+                defaultValue={this.props.GetValue()}
+                autoFocus={true}
+                onChange={this.onChange}
                 onKeyDown={this.onKeyDown}
                 onKeyPress={this.stopPropagation}
                 onPointerDown={this.stopPropagation}
@@ -220,7 +280,7 @@ export class EditableView extends React.Component<EditableProps> {
             <div
                 className={`editableView-container-editing${this.props.oneLine ? '-oneLine' : ''}`}
                 ref={this._ref}
-                style={{ display: this.props.display, textOverflow: this.props.overflow, minHeight: '10px', whiteSpace: 'nowrap', height: this.props.height || 'auto', maxHeight: this.props.maxHeight }}
+                style={{ display: this.props.display, textOverflow: this.props.overflow, minHeight: '10px', whiteSpace: this.props.oneLine ? 'nowrap' : 'pre-line', height: this.props.height, maxHeight: this.props.maxHeight }}
                 //onPointerDown={this.stopPropagation}
                 onClick={this.onClick}
                 placeholder={this.props.placeholder}>
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 8fd2b87cc..556d9b6a0 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -8,14 +8,13 @@ import { GetEffectiveAcl } from '../../fields/util';
 import { unimplementedFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { DragManager } from '../util/DragManager';
+import { FollowLinkScript } from '../util/LinkFollower';
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import './MarqueeAnnotator.scss';
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { AnchorMenu } from './pdf/AnchorMenu';
 import React = require('react');
-import { ScriptField } from '../../fields/ScriptField';
-import { FollowLinkScript } from '../util/LinkFollower';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
diff --git a/src/client/views/ScriptingRepl.scss b/src/client/views/ScriptingRepl.scss
index 778e9c445..adc82238e 100644
--- a/src/client/views/ScriptingRepl.scss
+++ b/src/client/views/ScriptingRepl.scss
@@ -31,6 +31,9 @@
     border-radius: 25%;
     padding: 2px;
 }
+.documentIcon-outerDiv:hover {
+    opacity: 0.3;
+}
 
 .scriptingObject-icon {
     padding: 3px;
@@ -48,4 +51,4 @@
 
 .scriptingObject-leaf {
     margin-left: 15px;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index 4fecfa4d9..5dfe10def 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -10,34 +10,49 @@ import { OverlayView } from './OverlayView';
 import './ScriptingRepl.scss';
 
 @observer
-export class ScriptingObjectDisplay extends React.Component<{ scrollToBottom: () => void, value: { [key: string]: any }, name?: string }> {
+export class ScriptingObjectDisplay extends React.Component<{ scrollToBottom: () => void; value: { [key: string]: any }; name?: string }> {
     @observable collapsed = true;
 
     @action
     toggle = () => {
         this.collapsed = !this.collapsed;
         this.props.scrollToBottom();
-    }
+    };
 
     render() {
         const val = this.props.value;
         const proto = Object.getPrototypeOf(val);
         const name = (proto && proto.constructor && proto.constructor.name) || String(val);
-        const title = this.props.name ? <><b>{this.props.name} : </b>{name}</> : name;
+        const title = this.props.name ? (
+            <>
+                <b>{this.props.name} : </b>
+                {name}
+            </>
+        ) : (
+            name
+        );
         if (this.collapsed) {
             return (
                 <div className="scriptingObject-collapsed">
-                    <span onClick={this.toggle} className="scriptingObject-icon scriptingObject-iconCollapsed"><FontAwesomeIcon icon="caret-right" size="sm" /></span>{title} (+{Object.keys(val).length})
+                    <span onClick={this.toggle} className="scriptingObject-icon scriptingObject-iconCollapsed">
+                        <FontAwesomeIcon icon="caret-right" size="sm" />
+                    </span>
+                    {title} (+{Object.keys(val).length})
                 </div>
             );
         } else {
             return (
                 <div className="scriptingObject-open">
                     <div>
-                        <span onClick={this.toggle} className="scriptingObject-icon"><FontAwesomeIcon icon="caret-down" size="sm" /></span>{title}
+                        <span onClick={this.toggle} className="scriptingObject-icon">
+                            <FontAwesomeIcon icon="caret-down" size="sm" />
+                        </span>
+                        {title}
                     </div>
                     <div className="scriptingObject-fields">
-                        {Object.keys(val).map(key => <ScriptingValueDisplay {...this.props} name={key} />)}
+                        {Object.keys(val).map(key => (
+                            <ScriptingValueDisplay {...this.props} name={key} />
+                        ))}
                     </div>
                 </div>
             );
@@ -46,18 +61,32 @@ export class ScriptingObjectDisplay extends React.Component<{ scrollToBottom: ()
 }
 
 @observer
-export class ScriptingValueDisplay extends React.Component<{ scrollToBottom: () => void, value: any, name?: string }> {
+export class ScriptingValueDisplay extends React.Component<{ scrollToBottom: () => void; value: any; name?: string }> {
     render() {
         const val = this.props.name ? this.props.value[this.props.name] : this.props.value;
-        if (typeof val === "object") {
+        if (typeof val === 'object') {
             return <ScriptingObjectDisplay scrollToBottom={this.props.scrollToBottom} value={val} name={this.props.name} />;
-        } else if (typeof val === "function") {
-            const name = "[Function]";
-            const title = this.props.name ? <><b>{this.props.name} : </b>{name}</> : name;
+        } else if (typeof val === 'function') {
+            const name = '[Function]';
+            const title = this.props.name ? (
+                <>
+                    <b>{this.props.name} : </b>
+                    {name}
+                </>
+            ) : (
+                name
+            );
             return <div className="scriptingObject-leaf">{title}</div>;
         } else {
             const name = String(val);
-            const title = this.props.name ? <><b>{this.props.name} : </b>{name}</> : name;
+            const title = this.props.name ? (
+                <>
+                    <b>{this.props.name} : </b>
+                    {name}
+                </>
+            ) : (
+                name
+            );
             return <div className="scriptingObject-leaf">{title}</div>;
         }
     }
@@ -65,11 +94,11 @@ export class ScriptingValueDisplay extends React.Component<{ scrollToBottom: ()
 
 @observer
 export class ScriptingRepl extends React.Component {
-    @observable private commands: { command: string, result: any }[] = [];
+    @observable private commands: { command: string; result: any }[] = [];
     private commandsHistory: string[] = [];
 
-    @observable private commandString: string = "";
-    private commandBuffer: string = "";
+    @observable private commandString: string = '';
+    private commandBuffer: string = '';
 
     @observable private historyIndex: number = -1;
 
@@ -82,7 +111,7 @@ export class ScriptingRepl extends React.Component {
             transformer: context => {
                 const knownVars: { [name: string]: number } = {};
                 const usedDocuments: number[] = [];
-                ScriptingGlobals.getGlobals().forEach((global: any) => knownVars[global] = 1);
+                ScriptingGlobals.getGlobals().forEach((global: any) => (knownVars[global] = 1));
                 return root => {
                     function visit(node: ts.Node) {
                         let skip = false;
@@ -105,7 +134,7 @@ export class ScriptingRepl extends React.Component {
                                     const m = parseInt(match[1]);
                                     usedDocuments.push(m);
                                 } else {
-                                    return ts.createPropertyAccess(ts.createIdentifier("args"), node);
+                                    return ts.createPropertyAccess(ts.createIdentifier('args'), node);
                                 }
                             }
                         }
@@ -114,20 +143,20 @@ export class ScriptingRepl extends React.Component {
                     }
                     return ts.visitNode(root, visit);
                 };
-            }
+            },
         };
-    }
+    };
 
     @action
     onKeyDown = (e: React.KeyboardEvent) => {
         let stopProp = true;
         switch (e.key) {
-            case "Enter": {
+            case 'Enter': {
                 e.stopPropagation();
                 const docGlobals: { [name: string]: any } = {};
-                Array.from(DocumentManager.Instance.DocumentViews).forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document);
+                DocumentManager.Instance.DocumentViews.forEach((dv, i) => (docGlobals[`d${i}`] = dv.props.Document));
                 const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
-                const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer(), globals });
+                const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: 'any' }, transformer: this.getTransformer(), globals });
                 if (!script.compiled) {
                     this.commands.push({ command: this.commandString, result: script.errors });
                     return;
@@ -139,13 +168,13 @@ export class ScriptingRepl extends React.Component {
 
                     this.maybeScrollToBottom();
 
-                    this.commandString = "";
-                    this.commandBuffer = "";
+                    this.commandString = '';
+                    this.commandBuffer = '';
                     this.historyIndex = -1;
                 }
                 break;
             }
-            case "ArrowUp": {
+            case 'ArrowUp': {
                 if (this.historyIndex < this.commands.length - 1) {
                     this.historyIndex++;
                     if (this.historyIndex === 0) {
@@ -155,12 +184,12 @@ export class ScriptingRepl extends React.Component {
                 }
                 break;
             }
-            case "ArrowDown": {
+            case 'ArrowDown': {
                 if (this.historyIndex >= 0) {
                     this.historyIndex--;
                     if (this.historyIndex === -1) {
                         this.commandString = this.commandBuffer;
-                        this.commandBuffer = "";
+                        this.commandBuffer = '';
                     } else {
                         this.commandString = this.commandsHistory[this.commands.length - 1 - this.historyIndex];
                     }
@@ -176,25 +205,25 @@ export class ScriptingRepl extends React.Component {
             e.stopPropagation();
             e.preventDefault();
         }
-    }
+    };
 
     @action
     onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.commandString = e.target.value;
-    }
+    };
 
     private shouldScroll: boolean = false;
     private maybeScrollToBottom = () => {
         const ele = this.commandsRef.current;
-        if (ele && ele.scrollTop === (ele.scrollHeight - ele.offsetHeight)) {
+        if (ele && ele.scrollTop === ele.scrollHeight - ele.offsetHeight) {
             this.shouldScroll = true;
             this.forceUpdate();
         }
-    }
+    };
 
     private scrollToBottom() {
         const ele = this.commandsRef.current;
-        ele && ele.scroll({ behavior: "auto", top: ele.scrollHeight });
+        ele && ele.scroll({ behavior: 'auto', top: ele.scrollHeight });
     }
 
     componentDidUpdate() {
@@ -206,15 +235,11 @@ export class ScriptingRepl extends React.Component {
 
     overlayDisposer?: () => void;
     onFocus = () => {
-        if (this.overlayDisposer) {
-            this.overlayDisposer();
-        }
+        this.overlayDisposer?.();
         this.overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
-    }
+    };
 
-    onBlur = () => {
-        this.overlayDisposer?.();
-    }
+    onBlur = () => this.overlayDisposer?.();
 
     render() {
         return (
@@ -229,14 +254,8 @@ export class ScriptingRepl extends React.Component {
                         );
                     })}
                 </div>
-                <input
-                    className="scriptingRepl-commandInput"
-                    onFocus={this.onFocus}
-                    onBlur={this.onBlur}
-                    value={this.commandString}
-                    onChange={this.onChange}
-                    onKeyDown={this.onKeyDown}></input>
+                <input className="scriptingRepl-commandInput" onFocus={this.onFocus} onBlur={this.onBlur} value={this.commandString} onChange={this.onChange} onKeyDown={this.onKeyDown}></input>
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index f3397e2c4..99a68e94b 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -372,7 +372,8 @@
         .editableView-container-editing-oneLine,
         .editableView-container-editing {
             color: grey;
-            padding-top: 10px;
+            padding-top: 5px;
+            padding-bottom: 5px;
             width: 100%;
         }
 
@@ -387,7 +388,8 @@
             letter-spacing: 2px;
             color: grey;
             border: 0px;
-            padding-top: 10px; // 12px 10px 11px 10px;
+            padding-top: 5px; // 12px 10px 11px 10px;
+            padding-bottom: 5px; // 12px 10px 11px 10px;
         }
     }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 29bdc0e2d..08e3174d1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -53,7 +53,6 @@ import { MarqueeView } from './MarqueeView';
 import React = require('react');
 
 export type collectionFreeformViewProps = {
-    noPointerWheel?: () => boolean; // turn off pointerwheel interactions (see PDFViewer)
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     originTopLeft?: boolean;
@@ -1023,13 +1022,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             deltaScale = 20 / invTransform.Scale;
         }
         if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
+            this.setPan(0, 0);
             return;
         }
         if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
             deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale;
         }
 
-        const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
+        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);
@@ -1039,17 +1039,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.props.noPointerWheel?.() || this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
+        if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
         PresBox.Instance?.pauseAutoPres();
         if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         e.stopPropagation();
-        e.preventDefault();
         switch (!e.ctrlKey ? Doc.UserDoc().freeformScrollMode : freeformScrollMode.Pan) {
             case freeformScrollMode.Pan:
                 // if ctrl is selected then zoom
                 if (e.ctrlKey) {
                     if (this.props.isContentActive(true)) {
-                        this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+                        if (this.props.isAnnotationOverlayScrollable) {
+                            // bcz: zooming on a webbox doesn't get the correct coordinates here for unknown reasons.
+                            // so better to do nothing than having things jump around.
+                        } else this.zoom(e.screenX, e.screenY, e.deltaY);
                     }
                 } // otherwise pan
                 else if (this.props.isContentActive(true)) {
@@ -1123,16 +1125,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, nativeHeight) +
                 (!this.props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
             let newPanY = Math.max(minPanY, Math.min(maxPanY, panY));
-            if (NumCast(this.rootDoc.scrollTop) && NumCast(this.rootDoc._viewScale, minScale) !== minScale) {
+            if (false && NumCast(this.rootDoc.scrollTop) && NumCast(this.rootDoc._viewScale, minScale) !== minScale) {
                 const relTop = NumCast(this.rootDoc.scrollTop) / maxScrollTop;
                 this.rootDoc.scrollTop = undefined;
                 newPanY = minPanY + relTop * (maxPanY - minPanY);
             } else if (fitYscroll && this.rootDoc.scrollTop === undefined && NumCast(this.rootDoc._viewScale, minScale) === minScale) {
                 const maxPanY = minPanY + fitYscroll;
                 const relTop = (panY - minPanY) / (maxPanY - minPanY);
-                setTimeout(() => {
-                    this.rootDoc.scrollTop = relTop * maxScrollTop;
-                }, 10);
+                setTimeout(() => (this.rootDoc.scrollTop = relTop * maxScrollTop), 10);
                 newPanY = minPanY;
             }
             !this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index a9434fde3..3a0c2c85c 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -182,8 +182,10 @@
 .schema-table-cell,
 .row-menu {
     border: 1px solid $medium-gray;
-    overflow: hidden;
+    overflow-x: hidden;
+    overflow-y: auto;
     padding: 5px;
+    display: inline-block;
 }
 
 .schema-row {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index a59d7e5a3..588affc1c 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -56,7 +56,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _documentOptions: DocumentOptions = new DocumentOptions();
     private _tableContentRef: HTMLDivElement | null = null;
 
-    public static _rowHeight: number = 50;
+    static _rowHeight: number = 50;
+    static _rowSingleLineHeight: number = 32;
     public static _minColWidth: number = 25;
     public static _rowMenuWidth: number = 60;
     public static _previewDividerWidth: number = 4;
@@ -515,9 +516,9 @@ export class CollectionSchemaView extends CollectionSubView() {
         if (found) {
             const rect = found.getBoundingClientRect();
             const localRect = this.props.ScreenToLocalTransform().transformBounds(rect.left, rect.top, rect.width, rect.height);
-            if (localRect.y < CollectionSchemaView._rowHeight || localRect.y + localRect.height > this.props.PanelHeight()) {
+            if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this.props.PanelHeight()) {
                 let focusSpeed = options.zoomTime ?? 50;
-                smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - CollectionSchemaView._rowHeight, options.easeFunc);
+                smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc);
                 return focusSpeed;
             }
         }
@@ -836,6 +837,7 @@ export class CollectionSchemaView extends CollectionSubView() {
               });
         return { docs };
     }
+    rowHeightFunc = () => (BoolCast(this.layoutDoc._singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
     sortedDocsFunc = () => this.sortedDocs;
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
@@ -850,7 +852,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                         // prevent wheel events from passively propagating up through containers
                         r?.addEventListener('wheel', (e: WheelEvent) => {}, { passive: false });
                     }}>
-                    <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
+                    <div className="schema-header-row" style={{ height: this.rowHeightFunc() }}>
                         <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}>
                             <div className="schema-header-button" onPointerDown={e => (this._columnMenuIndex === -1 ? this.closeColumnMenu() : this.openColumnMenu(-1, true))}>
                                 <FontAwesomeIcon icon="plus" />
@@ -865,6 +867,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 sortField={this.sortField}
                                 sortDesc={this.sortDesc}
                                 setSort={this.setSort}
+                                rowHeight={this.rowHeightFunc}
                                 removeColumn={this.removeColumn}
                                 resizeColumn={this.startResize}
                                 openContextMenu={this.openContextMenu}
@@ -875,7 +878,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                     </div>
                     {this._columnMenuIndex !== undefined && this.renderColumnMenu}
                     {this._filterColumnIndex !== undefined && this.renderFilterMenu}
-                    <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
+                    <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
                     <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} height={CollectionSchemaView._newNodeInputHeight} />
                 </div>
                 {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
@@ -922,20 +925,20 @@ interface CollectionSchemaViewDocsProps {
     schema: CollectionSchemaView;
     setRef: (ref: HTMLDivElement | null) => void;
     childDocs: () => { docs: Doc[] };
+    rowHeight: () => number;
 }
 
 @observer
 class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> {
     tableWidthFunc = () => this.props.schema.tableWidth;
-    rowHeightFunc = () => CollectionSchemaView._rowHeight;
-    childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -CollectionSchemaView._rowHeight - index * this.rowHeightFunc()));
+    childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -this.props.rowHeight() - index * this.props.rowHeight()));
     render() {
         return (
-            <div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + CollectionSchemaView._rowHeight}px)` }}>
+            <div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}>
                 {this.props.childDocs().docs.map((doc: Doc, index: number) => {
                     const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.schema.props.DataDoc;
                     return (
-                        <div className="schema-row-wrapper" style={{ height: CollectionSchemaView._rowHeight }}>
+                        <div className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
                             <DocumentView
                                 key={doc[Id]}
                                 {...this.props.schema.props}
@@ -945,7 +948,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 DataDoc={dataDoc}
                                 renderDepth={this.props.schema.props.renderDepth + 1}
                                 PanelWidth={this.tableWidthFunc}
-                                PanelHeight={this.rowHeightFunc}
+                                PanelHeight={this.props.rowHeight}
                                 styleProvider={DefaultStyleProvider}
                                 waitForDoubleClickToClick={returnNever}
                                 defaultDoubleClick={returnIgnore}
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 243fe0c61..7da3c042c 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -16,6 +16,7 @@ export interface SchemaColumnHeaderProps {
     sortDesc: boolean;
     setSort: (field: string | undefined, desc?: boolean) => void;
     removeColumn: (index: number) => void;
+    rowHeight: () => number;
     resizeColumn: (e: any, index: number) => void;
     dragColumn: (e: any, index: number) => boolean;
     openContextMenu: (x: number, y: number, index: number) => void;
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index ca9e0bda0..34631e0b7 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -2,6 +2,9 @@ import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import { Doc } from '../../../../fields/Doc';
+import { BoolCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { undoBatch } from '../../../util/UndoManager';
@@ -12,8 +15,6 @@ import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
-import { computedFn } from 'mobx-utils';
-import { Doc } from '../../../../fields/Doc';
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -95,7 +96,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className="schema-row"
-                style={{ height: CollectionSchemaView._rowHeight, backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined }}
+                style={{ height: this.props.PanelHeight(), backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined }}
                 onPointerEnter={this.onPointerEnter}
                 onPointerLeave={this.onPointerLeave}
                 ref={(row: HTMLDivElement | null) => {
@@ -132,13 +133,16 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             Document={this.rootDoc}
                             col={index}
                             fieldKey={key}
+                            allowCRs={false} // to enter text with new lines, must use \n
                             columnWidth={this.columnWidth(index)}
+                            rowHeight={this.schemaView.rowHeightFunc}
                             isRowActive={this.props.isContentActive}
                             getFinfo={this.getFinfo}
                             selectCell={this.selectCell}
                             deselectCell={this.deselectCell}
                             selectedCell={this.selectedCell}
                             setColumnValues={this.setColumnValues}
+                            oneLine={BoolCast(this.schemaDoc?._singleLine)}
                         />
                     ))}
                 </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 712bd4491..12daa1252 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -5,22 +5,24 @@ import { extname } from 'path';
 import DatePicker from 'react-datepicker';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field } from '../../../../fields/Doc';
+import { RichTextField } from '../../../../fields/RichTextField';
 import { BoolCast, Cast, DateCast, FieldValue } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
 import { FInfo } from '../../../documents/Documents';
+import { DocFocusOrOpen } from '../../../util/DocumentManager';
 import { dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
+import { OpenWhere } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { DefaultStyleProvider } from '../../StyleProvider';
-import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
+import { ColumnType, FInfotoColType } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
-import { RichTextField } from '../../../../fields/RichTextField';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 
 export interface SchemaTableCellProps {
     Document: Doc;
@@ -29,17 +31,24 @@ export interface SchemaTableCellProps {
     selectCell: (doc: Doc, col: number) => void;
     selectedCell: () => [Doc, number] | undefined;
     fieldKey: string;
+    maxWidth?: () => number;
     columnWidth: () => number;
+    rowHeight: () => number;
+    padding?: number; // default is 5 -- see scss
     isRowActive: () => boolean | undefined;
     getFinfo: (fieldKey: string) => FInfo | undefined;
     setColumnValues: (field: string, value: string) => boolean;
+    oneLine?: boolean; // whether all input should fit on one line vs allowing textare multiline inputs
+    allowCRs?: boolean; // allow carriage returns in text input (othewrise CR ends the edit)
+    finishEdit?: () => void; // notify container that edit is over (eg. to hide view in DashFieldView)
 }
 
 @observer
 export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
-    public static colRowHeightFunc() {
-        return CollectionSchemaView._rowHeight;
-    }
+    static addFieldDoc = (doc: Doc, where: OpenWhere) => {
+        DocFocusOrOpen(doc);
+        return true;
+    };
     public static renderProps(props: SchemaTableCellProps) {
         const { Document, fieldKey, getFinfo, columnWidth, isRowActive } = props;
         let protoCount = 0;
@@ -71,12 +80,12 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             whenChildContentsActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
-            addDocTab: returnFalse,
+            addDocTab: SchemaTableCell.addFieldDoc,
             pinToPres: returnZero,
             Document,
             fieldKey,
             PanelWidth: columnWidth,
-            PanelHeight: SchemaTableCell.colRowHeightFunc,
+            PanelHeight: props.rowHeight,
         };
         const readOnly = getFinfo(fieldKey)?.readOnly ?? false;
         const cursor = !readOnly ? 'text' : 'default';
@@ -98,8 +107,11 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                 style={{
                     color,
                     textDecoration,
+                    width: '100%',
                 }}>
                 <EditableView
+                    oneLine={this.props.oneLine}
+                    allowCRs={this.props.allowCRs}
                     contents={<FieldView {...fieldProps} />}
                     editing={this.selected ? undefined : false}
                     GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
@@ -107,7 +119,9 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                         if (shiftDown && enterKey) {
                             this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value);
                         }
-                        return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+                        const ret = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+                        this.props.finishEdit?.();
+                        return ret;
                     })}
                 />
             </div>
@@ -121,7 +135,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
         if (cellValue instanceof RichTextField) return ColumnType.RTF;
         if (typeof cellValue === 'number') return ColumnType.Any;
         if (typeof cellValue === 'string') return ColumnType.Any;
-        if (typeof cellValue === 'boolean') return ColumnType.Any;
+        if (typeof cellValue === 'boolean') return ColumnType.Boolean;
 
         const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType;
         if (columnTypeStr && columnTypeStr in FInfotoColType) {
@@ -148,7 +162,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             <div
                 className="schema-table-cell"
                 onPointerDown={action(e => !this.selected && this.props.selectCell(this.props.Document, this.props.col))}
-                style={{ width: this.props.columnWidth(), border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
+                style={{ padding: this.props.padding, maxWidth: this.props.maxWidth?.(), width: this.props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
                 {this.content}
             </div>
         );
@@ -211,8 +225,8 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
         const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio
         // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px
         // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
-        const height = CollectionSchemaView._rowHeight - 10;
-        const width = height * aspect; // increase the width of the image if necessary to maintain proportionality
+        const height = this.props.rowHeight() ? this.props.rowHeight() - (this.props.padding || 6) * 2 : undefined;
+        const width = height ? height * aspect : undefined; // increase the width of the image if necessary to maintain proportionality
 
         return <img src={this.url} width={width} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
     }
@@ -288,7 +302,9 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
                         if (shiftDown && enterKey) {
                             this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value);
                         }
-                        return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+                        const set = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+                        this.props.finishEdit?.();
+                        return set;
                     })}
                 />
             </div>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index a8c8a0d2e..862d0da8c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -12,12 +12,11 @@ import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { DocComponent } from '../DocComponent';
+import { InkingStroke } from '../InkingStroke';
 import { StyleProp } from '../StyleProvider';
 import './CollectionFreeFormDocumentView.scss';
 import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import React = require('react');
-import { DocumentType } from '../../documents/DocumentTypes';
-import { InkingStroke } from '../InkingStroke';
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; rotation?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined;
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 56de2d1fc..6e2ed72b8 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -1,23 +1,39 @@
-
-import { observer } from "mobx-react";
-import * as React from "react";
-import { DocumentView } from "./DocumentView";
-import { DocumentManager } from "../../util/DocumentManager";
-import { Transformer, ts } from "../../util/Scripting";
-import { Field } from "../../../fields/Doc";
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { DocumentView } from './DocumentView';
+import { DocumentManager } from '../../util/DocumentManager';
+import { Transformer, ts } from '../../util/Scripting';
+import { Field } from '../../../fields/Doc';
+import { Tooltip } from '@material-ui/core';
+import { action, observable } from 'mobx';
+import { Id } from '../../../fields/FieldSymbols';
+import { factory } from 'typescript';
+import { LightboxView } from '../LightboxView';
 
 @observer
-export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> {
+export class DocumentIcon extends React.Component<{ view: DocumentView; index: number }> {
+    @observable _hovered = false;
+    static get DocViews() {
+        return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.IsLightboxDocView(v.props.docViewPath())) : DocumentManager.Instance.DocumentViews;
+    }
     render() {
         const view = this.props.view;
         const { left, top, right, bottom } = view.getBounds() || { left: 0, top: 0, right: 0, bottom: 0 };
 
         return (
-            <div className="documentIcon-outerDiv" style={{
-                position: "absolute",
-                transform: `translate(${(left + right) / 2}px, ${top}px)`,
-            }}>
-                <p>d{this.props.index}</p>
+            <div
+                className="documentIcon-outerDiv"
+                onPointerEnter={action(e => (this._hovered = true))}
+                onPointerLeave={action(e => (this._hovered = false))}
+                style={{
+                    pointerEvents: 'all',
+                    opacity: this._hovered ? 0.3 : 1,
+                    position: 'absolute',
+                    transform: `translate(${(left + right) / 2}px, ${top}px)`,
+                }}>
+                <Tooltip title={<>{this.props.view.rootDoc.title}</>}>
+                    <p>d{this.props.index}</p>
+                </Tooltip>
             </div>
         );
     }
@@ -41,7 +57,9 @@ export class DocumentIconContainer extends React.Component {
                                 const match = node.text.match(/d([0-9]+)/);
                                 if (match) {
                                     const m = parseInt(match[1]);
+                                    const doc = DocumentIcon.DocViews[m].rootDoc;
                                     usedDocuments.add(m);
+                                    return factory.createIdentifier(`idToDoc("${doc[Id]}")`);
                                 }
                             }
                         }
@@ -52,14 +70,14 @@ export class DocumentIconContainer extends React.Component {
                 };
             },
             getVars() {
-                const docs = Array.from(DocumentManager.Instance.DocumentViews);
+                const docs = DocumentIcon.DocViews;
                 const capturedVariables: { [name: string]: Field } = {};
-                usedDocuments.forEach(index => capturedVariables[`d${index}`] = docs[index].props.Document);
-                return { capturedVariables };
-            }
+                usedDocuments.forEach(index => (capturedVariables[`d${index}`] = docs.length > index ? docs[index].props.Document : `d${index}`));
+                return capturedVariables;
+            },
         };
     }
     render() {
-        return Array.from(DocumentManager.Instance.DocumentViews).map((dv, i) => <DocumentIcon key={i} index={i} view={dv} />);
+        return DocumentIcon.DocViews.map((dv, i) => <DocumentIcon key={i} index={i} view={dv} />);
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b61a468bd..ffbde18ca 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,7 @@ import { PresEffect, PresEffectDirection } from './trails';
 import { PinProps, PresBox } from './trails/PresBox';
 import React = require('react');
 import { InkingStroke } from '../InkingStroke';
+import { RefField } from '../../../fields/RefField';
 const { Howl } = require('howler');
 
 interface Window {
@@ -127,6 +128,7 @@ export interface DocComponentView {
     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;
     incrementalRendering?: () => void;
     fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
@@ -434,7 +436,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         }, console.log  );
                     UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
                 } else if (!Doc.IsSystem(this.rootDoc) && (defaultDblclick === undefined || defaultDblclick === 'default')) {
-                    UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
+                    UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
                 } else {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index e317de11e..f3c8ba62d 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -20,6 +20,10 @@ import { ImageBox } from './ImageBox';
 import './KeyValueBox.scss';
 import { KeyValuePair } from './KeyValuePair';
 import React = require('react');
+import { DocumentManager } from '../../util/DocumentManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { ScriptingRepl } from '../ScriptingRepl';
+import { DocumentIconContainer } from './DocumentIcon';
 
 export type KVPScript = {
     script: CompiledScript;
@@ -75,7 +79,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         value = dubEq ? value.substring(2) : value;
         const options: ScriptOptions = { addReturn: true, typecheck: false, params: { this: Doc.name, self: Doc.name, _last_: 'any', _readOnly_: 'boolean' }, editable: true };
         if (dubEq) options.typecheck = false;
-        const script = CompileScript(value, options);
+        const script = CompileScript(value, { ...options, transformer: DocumentIconContainer.getTransformer() });
         return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq };
     }
 
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 5b660e582..57d36932e 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -1,60 +1,52 @@
-@import "../global/globalCssVariables";
-
+@import '../global/globalCssVariables';
 
 .keyValuePair-td-key {
-    display:inline-block;
+    display: inline-block;
 
-    .keyValuePair-td-key-container{
-        width:100%;
-        height:100%;
-        display: flex;                 
-        flex-direction: row;           
-        flex-wrap: nowrap;              
-        justify-content: space-between; 
-        align-items: center;
-        .keyValuePair-td-key-delete{
+    .keyValuePair-td-key-container {
+        width: 100%;
+        height: 100%;
+        display: flex;
+        flex-direction: row;
+        flex-wrap: nowrap;
+        justify-content: space-between;
+        .keyValuePair-td-key-delete {
             position: relative;
             background-color: transparent;
-            color:red;
+            color: red;
         }
         .keyValuePair-td-key-check {
             position: relative;
             margin: 0;
         }
         .keyValuePair-keyField {
-            width:100%;
+            width: 100%;
             margin-left: 20px;
-            margin-top: -1px;
             font-family: monospace;
-            // text-align: center;
-            align-self: center;
             position: relative;
             overflow: auto;
+            display: inline;
         }
     }
 }
 .keyValuePair-td-value {
-    display:inline-block;
+    display: inline-block;
     overflow: scroll;
     font-family: monospace;
     height: 30px;
-        .keyValuePair-td-value-container {
-            display: flex;
-            align-items: center;
-            align-content: center;
-            flex-direction: row;
-            justify-content: space-between;
-            flex-wrap: nowrap;
-            width: 100%;
-            height: 100%;
+    .keyValuePair-td-value-container {
+        display: inline;
+        justify-content: space-between;
+        width: 100%;
+        height: 100%;
 
-            img { 
-                max-height: 36px;
-                width: auto;
-            }
-            .videoBox-cont{
-                width: auto; 
-                max-height: 36px; 
-            }
+        img {
+            max-height: 36px;
+            width: auto;
+        }
+        .videoBox-cont {
+            width: auto;
+            max-height: 36px;
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 7ea6d42ff..85641124f 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -118,13 +118,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                 </td>
                 <td className="keyValuePair-td-value" style={{ width: `${100 - this.props.keyWidth}%` }} onContextMenu={this.onContextMenu}>
                     <div className="keyValuePair-td-value-container">
-                        <EditableView
-                            contents={contents}
-                            maxHeight={36}
-                            height={'auto'}
-                            GetValue={() => Field.toKeyValueString(props.Document, props.fieldKey)}
-                            SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)}
-                        />
+                        <EditableView contents={contents} GetValue={() => Field.toKeyValueString(props.Document, props.fieldKey)} SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)} />
                     </div>
                 </td>
             </tr>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e05b48c0b..82c8e796d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -2,16 +2,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as WebRequest from 'web-request';
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { HtmlField } from '../../../fields/HtmlField';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
-import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, getWordAtPoint, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, removeStyleSheetRule, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
@@ -31,12 +31,13 @@ import { Annotation } from '../pdf/Annotation';
 import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
+import { DocComponentView, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { LinkDocPreview } from './LinkDocPreview';
 import { PinProps, PresBox } from './trails';
 import './WebBox.scss';
 import React = require('react');
+import { RefField } from '../../../fields/RefField';
 const { CreateImage } = require('./WebBoxRenderer');
 const _global = (window /* browser */ || global) /* node */ as any;
 const htmlToText = require('html-to-text');
@@ -47,6 +48,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     public static openSidebarWidth = 250;
     public static sidebarResizerWidth = 5;
+    static webStyleSheet = addStyleSheet();
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }) => void);
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@@ -58,15 +60,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
     private _searchString = '';
+    private _scrollTimer: any;
 
     private get _getAnchor() {
         return AnchorMenu.Instance?.GetAnchor;
     }
-    @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.
+    @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't want the src parameter to also change as that would cause an unnecessary re-render.
     @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
     @observable private _searching: boolean = false;
     @observable private _showSidebar = false;
-    @observable private _scrollTimer: any;
     @observable private _webPageHasBeenRendered = false;
     @observable private _overlayAnnoInfo: Opt<Doc>;
     @observable private _marqueeing: number[] | undefined;
@@ -141,51 +143,40 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     updateThumb = async () => {
-        const imageBitmap = ImageCast(this.layoutDoc['thumb-frozen'])?.url.href;
+        if (!this._iframe) return;
         const scrollTop = NumCast(this.layoutDoc._scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
         const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
-        if (
-            !this.props.isSelected(true) &&
-            !Doc.IsBrushedDegree(this.rootDoc) &&
-            !this.isAnyChildContentActive() &&
-            !this.rootDoc.thumbLockout &&
-            !this.props.dontRegisterView &&
-            this._iframe &&
-            !imageBitmap &&
-            (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight)
-        ) {
-            var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
-            if (!htmlString) {
-                htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
-            }
-            this.layoutDoc.thumb = undefined;
-            this.rootDoc.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')) {
-                        console.log('BAD DATA IN THUMB CREATION');
-                        return;
-                    }
-                    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.layoutDoc.thumb = new ImageField(returnedfilename);
-                                this.layoutDoc.thumbScrollTop = scrollTop;
-                                this.layoutDoc.thumbNativeWidth = nativeWidth;
-                                this.layoutDoc.thumbNativeHeight = nativeHeight;
-                            }),
-                            500
-                        )
-                    );
-                })
-                .catch(function (error: any) {
-                    console.error('oops, something went wrong!', error);
-                });
+        var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
+        if (!htmlString) {
+            htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
         }
+        this.layoutDoc.thumb = undefined;
+        this.rootDoc.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')) {
+                    console.log('BAD DATA IN THUMB CREATION');
+                    return;
+                }
+                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.layoutDoc.thumb = new ImageField(returnedfilename);
+                            this.layoutDoc.thumbScrollTop = scrollTop;
+                            this.layoutDoc.thumbNativeWidth = nativeWidth;
+                            this.layoutDoc.thumbNativeHeight = nativeHeight;
+                        }),
+                        500
+                    )
+                );
+            })
+            .catch(function (error: any) {
+                console.error('oops, something went wrong!', error);
+            });
     };
-    _thumbTimer: any;
+
     async componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
@@ -196,24 +187,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"])`;
             reqdFuncs[this.fieldKey + '-annotations-setter'] = `this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"] = value`;
             reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"])`;
-            DocUtils.AssignScripts(this.rootDoc, {}, reqdFuncs);
+            DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
-        reaction(
-            () => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
-            async selected => {
-                if (selected) {
-                    this._thumbTimer && clearTimeout(this._thumbTimer);
-                    this._webPageHasBeenRendered = true;
-                } else if (
-                    (!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
-                    LightboxView.LightboxDoc !== this.rootDoc
-                ) {
-                    // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
-                    this._thumbTimer && clearTimeout(this._thumbTimer);
-                    this._thumbTimer = setTimeout(this.updateThumb, 2000);
-                }
-            },
-            { fireImmediately: this.props.isSelected(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(this.props.Document) ? true : false) }
+        this._disposers.urlchange = reaction(
+            () => WebCast(this.rootDoc.data),
+            url => {
+                this.submitURL(url.url.href, false, false);
+            }
         );
 
         this._disposers.autoHeight = reaction(
@@ -296,8 +276,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._savedAnnotations;
     };
 
-    menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
-
     setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
     brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
     focus = (anchor: Doc, options: DocFocusOptions) => {
@@ -316,9 +294,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     };
 
-    getView = async (doc: Doc) => {
+    @action
+    getView = (doc: Doc) => {
         if (this.rootDoc.layoutKey === 'layout_icon') this.props.DocumentView?.().iconify();
-        if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl));
+        if (this._url && WebCast(doc.presData).url.href !== this._url) this.setData(WebCast(doc.presData).url.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
     };
@@ -341,13 +320,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         } catch (e) {}
         const anchor =
             this._getAnchor(this._savedAnnotations, false) ??
-            Docs.Create.WebanchorDocument(this._url, {
+            Docs.Create.WebanchorDocument({
                 title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
                 y: NumCast(this.layoutDoc._scrollTop),
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
-        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
         anchor.textHtml = ele?.innerHTML;
         //addAsAnnotation &&
@@ -413,7 +392,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     iframeClick = () => this._iframeClick;
     iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale;
 
-    addStyleSheet(document: any, styleType: string = 'text/css') {
+    addWebStyleSheet(document: any, styleType: string = 'text/css') {
         if (document) {
             const style = document.createElement('style');
             style.type = styleType;
@@ -421,7 +400,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             return (sheets as any).sheet;
         }
     }
-    addStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') {
+    addWebStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') {
         const propText =
             typeof css === 'string'
                 ? css
@@ -438,8 +417,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this._initialScroll !== undefined) {
             this.setScrollPos(this._initialScroll);
         }
-
-        this.addStyleSheetRule(this.addStyleSheet(this._iframe?.contentDocument), '::selection', { color: 'white', background: 'orange' }, '');
+        this._scrollHeight = this._iframe?.contentDocument?.body?.scrollHeight ?? 0;
+        this.addWebStyleSheetRule(this.addWebStyleSheet(this._iframe?.contentDocument), '::selection', { color: 'white', background: 'orange' }, '');
 
         let href: Opt<string>;
         try {
@@ -463,12 +442,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     .replace('search&', 'search?')
                     .replace('?gbv=1', '');
             }
-            this.submitURL(requrlraw, undefined, true);
+            this.setData(requrlraw);
         }
         const iframeContent = iframe?.contentDocument;
         if (iframeContent) {
             iframeContent.addEventListener('pointerup', this.iframeUp);
             iframeContent.addEventListener('pointerdown', this.iframeDown);
+            // iframeContent.addEventListener(
+            //     'wheel',
+            //     e => {
+            //         e.ctrlKey && e.preventDefault();
+            //     },
+            //     { passive: false }
+            // );
             const initHeights = () => {
                 this._scrollHeight = Math.max(this._scrollHeight, (iframeContent.body.children[0] as any)?.scrollHeight || 0);
                 if (this._scrollHeight) {
@@ -482,11 +468,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 action(() => initHeights),
                 5000
             );
-            iframe.setAttribute('enable-annotation', 'true');
             iframeContent.addEventListener(
                 'click',
                 undoBatch(
                     action((e: MouseEvent) => {
+                        const batch = UndoManager.StartBatch('webclick');
                         let href = '';
                         for (let ele = e.target as any; ele; ele = ele.parentElement) {
                             href = (typeof ele.href === 'string' ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
@@ -494,7 +480,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         const origin = this.webField?.origin;
                         if (href && origin) {
                             e.stopPropagation();
-                            setTimeout(() => this.submitURL(href.replace(Utils.prepend(''), origin)));
+                            setTimeout(() => {
+                                this.setData(href.replace(Utils.prepend(''), origin));
+                                batch.end();
+                            });
                             if (this._outerRef.current) {
                                 this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
                                 this._outerRef.current.scrollLeft = 0;
@@ -503,39 +492,49 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     })
                 )
             );
-            iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
-            //iframe.contentDocument.addEventListener('scroll', () => !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false));
+            iframe.contentDocument.addEventListener('wheel', this.iframeWheel, { passive: false });
         }
     };
 
     @action
-    iframeWheel = (e: any) => {
+    iframeWheel = (e: WheelEvent) => {
         if (!this._scrollTimer) {
-            this._scrollTimer = setTimeout(
-                action(() => (this._scrollTimer = undefined)),
-                250
-            ); // this turns events off on the iframe which allows scrolling to change direction smoothly
+            addStyleSheetRule(WebBox.webStyleSheet, 'webBox-iframe', { 'pointer-events': 'none' });
+            this._scrollTimer = setTimeout(() => {
+                this._scrollTimer = undefined;
+                clearStyleSheetRules(WebBox.webStyleSheet);
+            }, 250); // this turns events off on the iframe which allows scrolling to change direction smoothly
+        }
+        if (e.ctrlKey) {
+            if (this._innerCollectionView) {
+                this._innerCollectionView.zoom(e.screenX, e.screenY, e.deltaY);
+                const offset = e.clientY - NumCast(this.layoutDoc._scrollTop);
+                this.layoutDoc.panY = offset - offset / NumCast(this.layoutDoc._viewScale) + NumCast(this.layoutDoc._scrollTop) - NumCast(this.layoutDoc._scrollTop) / NumCast(this.layoutDoc._viewScale);
+            }
+            e.preventDefault();
         }
     };
 
     @action
     setDashScrollTop = (scrollTop: number, timeout: number = 250) => {
         const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
-        this._scrollTimer && clearTimeout(this._scrollTimer);
-        this._scrollTimer = setTimeout(
-            action(() => {
-                this._scrollTimer = undefined;
-                const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
-                if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
-                    this.layoutDoc.thumb = undefined;
-                    this.layoutDoc.thumbScrollTop = undefined;
-                    this.layoutDoc.thumbNativeWidth = undefined;
-                    this.layoutDoc.thumbNativeHeight = undefined;
-                    this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
-                } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
-            }),
-            timeout
-        );
+        if (this._scrollTimer) {
+            clearTimeout(this._scrollTimer);
+            clearStyleSheetRules(WebBox.webStyleSheet);
+        }
+        addStyleSheetRule(WebBox.webStyleSheet, 'webBox-iframe', { 'pointer-events': 'none' });
+        this._scrollTimer = setTimeout(() => {
+            clearStyleSheetRules(WebBox.webStyleSheet);
+            this._scrollTimer = undefined;
+            const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
+            if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
+                this.layoutDoc.thumb = undefined;
+                this.layoutDoc.thumbScrollTop = undefined;
+                this.layoutDoc.thumbNativeWidth = undefined;
+                this.layoutDoc.thumbNativeHeight = undefined;
+                this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
+            } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
+        }, timeout);
     };
 
     goTo = (scrollTop: number, duration: number, easeFunc: 'linear' | 'ease' | undefined) => {
@@ -557,8 +556,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         runInAction(() => {
             if (future.length) {
                 const curUrl = this._url;
-                this.rootDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
-                this.rootDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
+                this.dataDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
+                this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
+                this._scrollHeight = 0;
                 if (this._webUrl === this._url) {
                     this._webUrl = curUrl;
                     setTimeout(action(() => (this._webUrl = this._url)));
@@ -578,9 +578,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         runInAction(() => {
             if (history.length) {
                 const curUrl = this._url;
-                if (future === undefined) this.rootDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
-                else this.rootDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
-                this.layoutDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
+                if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
+                else this.dataDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
+                this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
+                this._scrollHeight = 0;
                 if (this._webUrl === this._url) {
                     this._webUrl = curUrl;
                     setTimeout(action(() => (this._webUrl = this._url)));
@@ -607,23 +608,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!newUrl) return;
         if (!newUrl.startsWith('http')) newUrl = 'http://' + newUrl;
         try {
-            const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'));
-            const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'));
-            const url = this.webField?.toString();
-            if (url && !preview) {
-                this.rootDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), url]);
-                this.layoutDoc._scrollTop = 0;
+            if (!preview) {
                 if (this._webPageHasBeenRendered) {
                     this.layoutDoc.thumb = undefined;
                     this.layoutDoc.thumbScrollTop = undefined;
                     this.layoutDoc.thumbNativeWidth = undefined;
                     this.layoutDoc.thumbNativeHeight = undefined;
                 }
-                future && (future.length = 0);
             }
             if (!preview) {
-                this.layoutDoc[this.fieldKey] = new WebField(new URL(newUrl));
-                !dontUpdateIframe && (this._webUrl = this._url);
+                if (!dontUpdateIframe) {
+                    this._webUrl = this._url;
+                }
             }
         } catch (e) {
             console.log('WebBox URL error:' + this._url);
@@ -637,48 +633,28 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const uri = dataTransfer.getData('text/uri-list');
         const url = uri || html || this._url || '';
         const newurl = url.startsWith(window.location.origin) ? url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^\/]*/)?.[0] || '') : url;
-        this.submitURL(newurl);
+        this.setData(newurl);
         e.stopPropagation();
     };
+
+    @action
+    setData = (data: Field | Promise<RefField | undefined>) => {
+        if (!(typeof data === 'string') && !(data instanceof WebField)) return false;
+        if (Field.toString(data) === this._url) return false;
+        this._scrollHeight = 0;
+        const oldUrl = this._url;
+        const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []);
+        const weburl = new WebField(Field.toString(data));
+        this.dataDoc[this.fieldKey + '-future'] = new List<string>([]);
+        this.dataDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), oldUrl]);
+        this.dataDoc[this.fieldKey] = weburl;
+        return true;
+    };
     onWebUrlValueKeyDown = (e: React.KeyboardEvent) => {
-        e.key === 'Enter' && this.submitURL(this._keyInput.current!.value);
+        if (e.key === 'Enter') this.setData(this._keyInput.current!.value);
         e.stopPropagation();
     };
 
-    @computed get urlEditor() {
-        return (
-            <div className="collectionMenu-webUrlButtons" onDrop={this.onWebUrlDrop} onDragOver={e => e.preventDefault()}>
-                <input
-                    className="collectionMenu-urlInput"
-                    key={this._url}
-                    placeholder="ENTER URL"
-                    defaultValue={this._url}
-                    onDrop={this.onWebUrlDrop}
-                    onDragOver={e => e.preventDefault()}
-                    onKeyDown={this.onWebUrlValueKeyDown}
-                    onClick={e => {
-                        this._keyInput.current!.select();
-                        e.stopPropagation();
-                    }}
-                    ref={this._keyInput}
-                />
-                <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', maxWidth: '250px' }}>
-                    <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}>
-                        GO
-                    </button>
-                    <button className="submitUrl" onClick={() => this.back}>
-                        {' '}
-                        <FontAwesomeIcon icon="caret-left" size="lg" />{' '}
-                    </button>
-                    <button className="submitUrl" onClick={() => this.forward}>
-                        {' '}
-                        <FontAwesomeIcon icon="caret-right" size="lg" />{' '}
-                    </button>
-                </div>
-            </div>
-        );
-    }
-
     specificContextMenu = (e: React.MouseEvent | PointerEvent): void => {
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
@@ -706,6 +682,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 },
                 icon: 'snowflake',
             });
+            funcs.push({ description: 'Create Thumbnail', event: () => this.updateThumb(), icon: 'portrait' });
             cm.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
         }
     };
@@ -746,19 +723,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     @computed get urlContent() {
-        if (this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc)) return null;
-        this.props.thumbShown?.();
+        setTimeout(
+            action(() => {
+                if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
+                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+                }
+                this._webPageHasBeenRendered = true;
+            })
+        );
         const field = this.rootDoc[this.props.fieldKey];
-        let view;
         if (field instanceof HtmlField) {
-            view = <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
-        } else if (field instanceof WebField) {
+            return <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
+        }
+        if (field instanceof WebField) {
             const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl;
-            view = (
+            return (
                 <iframe
                     className="webBox-iframe"
-                    enable-annotation={'true'}
-                    style={{ pointerEvents: this._scrollTimer ? 'none' : undefined }}
                     ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
                     src={url}
                     onLoad={this.iframeLoaded}
@@ -767,32 +748,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     sandbox={`${this.layoutDoc.allowScripts ? 'allow-scripts' : ''} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`}
                 />
             );
-        } else {
-            view = (
-                <iframe
-                    className="webBox-iframe"
-                    enable-annotation={'true'}
-                    style={{ pointerEvents: this._scrollTimer ? 'none' : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
-                    ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
-                    src={'https://crossorigin.me/https://cs.brown.edu'}
-                />
-            );
         }
-        setTimeout(
-            action(() => {
-                this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
-                if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
-                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
-                }
-                this._webPageHasBeenRendered = true;
-            })
-        );
-        return view;
+        return <iframe className="webBox-iframe" ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))} src={'https://crossorigin.me/https://cs.brown.edu'} />;
     }
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
-        console.log(annotationKey);
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.webUrl = this._url));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.presData = new WebField(this._url)));
         return this.addDocument(doc, annotationKey);
     };
 
@@ -843,7 +804,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.layoutDoc._showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={e => this.sidebarBtnDown(e, true)}>
@@ -878,17 +839,33 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
     });
+    @action
+    onZoomWheel = (e: React.WheelEvent) => {
+        if (this.props.isContentActive(true)) {
+            e.stopPropagation();
+        }
+    };
     sidebarWidth = () => {
         if (!this.SidebarShown) return 0;
         if (this._previewWidth) return WebBox.sidebarResizerWidth + WebBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
         const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
         return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
     };
+    _innerCollectionView: CollectionFreeFormView | undefined;
+    zoomScaling = () => this._innerCollectionView?.zoomScaling() ?? 1;
+    setInnerContent = (component: DocComponentView) => (this._innerCollectionView = component as CollectionFreeFormView);
+
     @computed get content() {
         const interactive = this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None;
         return (
-            <div className={'webBox-cont' + (interactive ? '-interactive' : '')} onKeyDown={e => e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%' }}>
-                {this.urlContent}
+            <div
+                className={'webBox-cont' + (interactive ? '-interactive' : '')}
+                onKeyDown={e => e.stopPropagation()}
+                style={{
+                    width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%',
+                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.panX)}px, ${-NumCast(this.layoutDoc.panY)}px)`,
+                }}>
+                {this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc) ? null : this.urlContent}
             </div>
         );
     }
@@ -896,7 +873,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get annotationLayer() {
         TraceMobx();
         return (
-            <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+            <div
+                className="webBox-annotationLayer"
+                style={{
+                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.panX)}px, ${-NumCast(this.layoutDoc.panY)}px)`,
+                    height: Doc.NativeHeight(this.Document) || undefined,
+                }}
+                ref={this._annotationLayer}>
                 {this.inlineTextAnnotations
                     .sort((a, b) => NumCast(a.y) - NumCast(b.y))
                     .map(anno => (
@@ -916,9 +899,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const renderAnnotations = (docFilters: () => string[]) => (
             <CollectionFreeFormView
                 {...this.props}
-                setContentView={emptyFunction}
+                setContentView={this.setInnerContent}
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
+                originTopLeft={false}
+                isAnnotationOverlayScrollable={true}
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
                 fieldKey={this.annotationKey}
@@ -945,16 +930,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
         return (
             <div
-                className={'webBox-outerContent'}
+                className="webBox-outerContent"
                 ref={this._outerRef}
                 style={{
                     height: `${100 / scale}%`,
                     pointerEvents,
                 }}
-                onWheel={StopEvent} // block wheel events from propagating since they're handled by the iframe
+                // when active, block wheel events from propagating since they're handled by the iframe
+                onWheel={this.onZoomWheel}
                 onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
                 onPointerDown={this.onMarqueeDown}>
-                <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered && this._scrollHeight ? this.scrollHeight : '100%', pointerEvents }}>
+                <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight) || '100%', pointerEvents }}>
                     {this.content}
                     {<div style={{ display: DragManager.docsBeingDragged.length ? 'none' : undefined, mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>}
                     {renderAnnotations(this.opaqueFilter)}
@@ -1016,7 +1002,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance?.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
     annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none');
     render() {
-        setTimeout(() => DocListCast(this.rootDoc[this.annotationKey]).forEach(doc => (doc.webUrl = this._url)));
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 26515da30..a4968dcd0 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -505,7 +505,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             <div className="menuButton editableText">
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} />
                 <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}>
-                    <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} contents={checkResult} />
+                    <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
                 </div>
             </div>
         );
@@ -889,7 +889,7 @@ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
         if (checkResult) {
             return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href);
         }
-        (selected.ComponentView as WebBox).submitURL(url);
+        selected.ComponentView?.setData?.(url);
         //selected.rootDoc.data = new WebField(url);
     }
 });
@@ -925,6 +925,15 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
         }
     }
 });
+ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
+    const selected = SelectionManager.Docs().lastElement();
+    if (checkResult && selected) {
+        return NumCast(selected._singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
+    }
+    if (selected) {
+        selected._singleLine = !selected._singleLine;
+    }
+});
 
 /** STACK
  *      groupBy
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index bf6fa2ec6..a9e59ab1c 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -2,18 +2,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { NodeSelection } from 'prosemirror-state';
 import * as ReactDOM from 'react-dom/client';
-import { DataSym, Doc, DocListCast, Field } from '../../../../fields/Doc';
+import { DataSym, Doc, Field } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { ComputedField } from '../../../../fields/ScriptField';
 import { Cast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell';
 import { OpenWhere } from '../DocumentView';
 import './DashFieldView.scss';
 import { FormattedTextBox } from './FormattedTextBox';
@@ -70,10 +69,10 @@ export class DashFieldView {
             } catch {}
         });
     }
-    deselectNode() {
+    @action deselectNode() {
         this.dom.classList.remove('ProseMirror-selectednode');
     }
-    selectNode() {
+    @action selectNode() {
         this.dom.classList.add('ProseMirror-selectednode');
     }
 }
@@ -98,6 +97,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     _fieldKey: string;
     _fieldStringRef = React.createRef<HTMLSpanElement>();
     @observable _dashDoc: Doc | undefined;
+    @observable _expanded = false;
 
     constructor(props: IDashFieldViewInternal) {
         super(props);
@@ -114,126 +114,32 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._reactionDisposer?.();
     }
 
-    public static multiValueDelimeter = ';';
-    public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
-        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? '';
-        const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(':=') || dashVal === '' ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
-        return { boolVal: Cast(fval, 'boolean', null), strVal: Field.toString(fval as Field) || '' };
-    }
-
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
-        if (this._dashDoc) {
-            const { boolVal, strVal } = DashFieldViewInternal.fieldContent(this._textBoxDoc, this._dashDoc, this._fieldKey);
-            // field value is a boolean, so use a checkbox or similar widget to display it
-            if (boolVal === true || boolVal === false) {
-                return (
-                    <input
-                        className="dashFieldView-fieldCheck"
-                        type="checkbox"
-                        checked={boolVal}
-                        onChange={e => {
-                            if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = e.target.checked;
-                            Doc.SetInPlace(this._dashDoc!, this._fieldKey, e.target.checked, true);
-                        }}
-                    />
-                );
-            } // field value is a string, so display it as an editable span
-            else {
-                // bcz: this is unfortunate, but since this React component is nested within a non-React text box (prosemirror), we can't
-                // use React events.  Essentially, React events occur after native events have been processed, so corresponding React events
-                // will never fire because Prosemirror has handled the native events.  So we add listeners for native events here.
-                return (
-                    <span
-                        className="dashFieldView-fieldSpan"
-                        contentEditable={!this.props.unclickable()}
-                        style={{ display: strVal.length < 2 ? 'inline-block' : undefined }}
-                        suppressContentEditableWarning={true}
-                        defaultValue={strVal}
-                        ref={r => {
-                            r?.addEventListener('keydown', e => this.fieldSpanKeyDown(e, r));
-                            r?.addEventListener('blur', e => r && this.updateText(r.textContent!, false));
-                            r?.addEventListener(
-                                'pointerdown',
-                                action(e => {
-                                    // let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
-                                    // while (target && !target.dataset?.targethrefs) target = target.parentElement;
-                                    this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new NodeSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos()))));
-                                    // FormattedTextBoxComment.update(this.props.tbox, this.props.tbox.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc);
-                                    // e.stopPropagation();
-                                })
-                            );
-                        }}>
-                        {strVal}
-                    </span>
-                );
-            }
-        }
+        return !this._dashDoc ? null : (
+            <div onClick={action(e => (this._expanded = !this.props.editable ? !this._expanded : true))} style={{ fontSize: 'smaller', width: this.props.hideKey ? this.props.tbox.props.PanelWidth() - 20 : undefined }}>
+                <SchemaTableCell
+                    Document={this._dashDoc}
+                    col={0}
+                    deselectCell={emptyFunction}
+                    selectCell={emptyFunction}
+                    maxWidth={this.props.hideKey ? undefined : () => 100}
+                    columnWidth={this.props.hideKey ? () => this.props.tbox.props.PanelWidth() - 20 : returnZero}
+                    selectedCell={() => [this._dashDoc!, 0]}
+                    fieldKey={this._fieldKey}
+                    rowHeight={returnZero}
+                    isRowActive={() => this._expanded && this.props.editable}
+                    padding={0}
+                    getFinfo={emptyFunction}
+                    setColumnValues={returnFalse}
+                    allowCRs={true}
+                    oneLine={!this._expanded}
+                    finishEdit={action(() => (this._expanded = false))}
+                />
+            </div>
+        );
     }
 
-    // we need to handle all key events on the input span or else they will propagate to prosemirror.
-    @action
-    fieldSpanKeyDown = (e: KeyboardEvent, span: HTMLSpanElement) => {
-        if (e.key === 'c' && (e.ctrlKey || e.metaKey)) {
-            navigator.clipboard.writeText(window.getSelection()?.toString() || '');
-            return;
-        }
-        if (e.key === 'Enter') {
-            // handle the enter key by "submitting" the current text to Dash's database.
-            this.updateText(span.textContent!, true);
-            e.preventDefault(); // prevent default to avoid a newline from being generated and wiping out this field view
-        }
-        if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
-            // handle ctrl-A to select all the text within the span
-            if (window.getSelection) {
-                const range = document.createRange();
-                range.selectNodeContents(span);
-                window.getSelection()!.removeAllRanges();
-                window.getSelection()!.addRange(range);
-            }
-            e.preventDefault(); //prevent default so that all the text in the prosemirror text box isn't selected
-        }
-        if (!this.props.editable) {
-            e.preventDefault();
-        }
-        e.stopPropagation(); // we need to handle all events or else they will propagate to prosemirror.
-    };
-
-    @action
-    updateText = (nodeText: string, forceMatch: boolean) => {
-        if (nodeText) {
-            const newText = nodeText.startsWith(':=') || nodeText.startsWith('=:=') ? ':=-computed-' : nodeText;
-            // look for a document whose id === the fieldKey being displayed.  If there's a match, then that document
-            // holds the different enumerated values for the field in the titles of its collected documents.
-            // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down.
-            DocServer.GetRefField(this._fieldKey).then(options => {
-                let modText = '';
-                options instanceof Doc && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title)));
-                if (modText) {
-                    //  elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText;
-                    Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true);
-                } // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
-                else if (nodeText.startsWith(':=')) {
-                    this._dashDoc![DataSym][this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
-                } else if (nodeText.startsWith('=:=')) {
-                    Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3));
-                } else {
-                    if (Number(newText).toString() === newText) {
-                        if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
-                        Doc.SetInPlace(this._dashDoc!, this._fieldKey, Number(newText), true);
-                    } else {
-                        const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
-                        if (!this._textBoxDoc[this._fieldKey]) {
-                            const strVal = splits.length > 1 ? new List<string>(splits) : newText;
-                            if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
-                            Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true);
-                        }
-                    }
-                }
-            });
-        }
-    };
-
     createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement();
         if (container) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index b5aa34a29..68160ac9b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1575,7 +1575,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
         else if (this.props.isContentActive(true)) {
             const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
-            !this.props.isSelected(true) && editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(pcords?.pos || 0))));
+            // !this.props.isSelected(true) &&
+            editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(pcords?.pos || 0))));
             let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
             while (target && !target.dataset?.targethrefs) target = target.parentElement;
             FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 5b47e8a70..7e17008bb 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -46,7 +46,7 @@ export const marks: { [index: string]: MarkSpec } = {
         toDOM(node: any) {
             const targethrefs = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.href : item.href), '');
             const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.anchorId : item.anchorId), '');
-            return ['a', { class: anchorids, 'data-targethrefs': targethrefs, 'data-linkdoc': node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
+            return ['a', { class: anchorids, 'data-targethrefs': targethrefs, 'data-noPreview': 'true', 'data-linkdoc': node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
         },
     },
     noAutoLinkAnchor: {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index bd2be8f11..c22107c03 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -5,7 +5,7 @@ import { action, computed, IReactionDisposer, observable, ObservableSet, reactio
 import { observer } from 'mobx-react';
 import { AnimationSym, Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../../fields/Doc';
 import { Copy, Id } from '../../../../fields/FieldSymbols';
-import { InkField, InkTool } from '../../../../fields/InkField';
+import { InkField } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
 import { ObjectField } from '../../../../fields/ObjectField';
 import { listSpec } from '../../../../fields/Schema';
@@ -429,6 +429,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }
         }
+        if ((pinDataTypes?.dataview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
+            bestTarget._dataTransition = `all ${transTime}ms`;
+            const fkey = Doc.LayoutFieldKey(bestTarget);
+            const setData = bestTargetView?.ComponentView?.setData;
+            if (setData) setData(activeItem.presData);
+            else Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+            bestTarget[fkey + '-usePath'] = activeItem.presUsePath;
+            setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
+        }
         if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) {
             if (bestTarget.xRange !== activeItem.presXRange) {
                 bestTarget.xRange = (activeItem.presXRange as ObjectField)?.[Copy]();
@@ -521,13 +530,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
             Doc.GetProto(bestTarget)[fkey + '-annotations'] = newList;
         }
-        if ((pinDataTypes?.dataview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
-            bestTarget._dataTransition = `all ${transTime}ms`;
-            const fkey = Doc.LayoutFieldKey(bestTarget);
-            Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
-            bestTarget[fkey + '-usePath'] = activeItem.presUsePath;
-            setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
-        }
         if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) {
             changed = true;
             const layoutField = Doc.LayoutFieldKey(bestTarget);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index fce67e7fc..ea709a859 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -528,7 +528,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
             style={{
                 mixBlendMode: mixBlendMode,
                 display: display,
-                transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
                 pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined,
             }}>
             <CollectionFreeFormView
@@ -537,7 +536,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 NativeHeight={returnZero}
                 setContentView={emptyFunction} // override setContentView to do nothing
                 pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it.
-                childPointerEvents={'all'} // but freeform children need to get events to allow text editing, etc
+                childPointerEvents="all" // but freeform children need to get events to allow text editing, etc
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
                 fieldKey={this.props.fieldKey + '-annotations'}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index b8ac8fb5d..5e3e27a7c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -41,7 +41,7 @@ export namespace Field {
         switch (typeof field) {
             case 'string':
                 if (field.startsWith('{"')) return `'${field}'`; // bcz: hack ... want to quote the string the right way. if there are nested "'s, then use ' instead of ".  In this case, test for the start of a JSON string of the format {"property": ... } and use outer 's instead of "s
-                return `"${field}"`;
+                return !field.includes('`') ? `\`${field}\`` : `"${field}"`;
             case 'number':
             case 'boolean':
                 return String(field);
-- 
cgit v1.2.3-70-g09d2


From 42afc0250de658fc3e924864bfae5afb4edec335 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 14 May 2023 12:03:40 -0400
Subject: major overhaul of field naming conventions.

---
 .../apis/google_docs/GooglePhotosClientUtils.ts    |  88 +++---
 src/client/documents/DocumentTypes.ts              |   2 +-
 src/client/documents/Documents.ts                  | 324 ++++++++++-----------
 src/client/documents/Gitlike.ts                    |  26 +-
 src/client/util/CaptureManager.tsx                 |   2 +-
 src/client/util/CurrentUserUtils.ts                | 179 ++++++------
 src/client/util/DictationManager.ts                |   2 +-
 src/client/util/DocumentManager.ts                 |  22 +-
 src/client/util/DragManager.ts                     |  12 +-
 src/client/util/DropConverter.ts                   |   4 +-
 src/client/util/GroupManager.tsx                   |   8 +-
 src/client/util/HypothesisUtils.ts                 |   2 +-
 src/client/util/Import & Export/ImageUtils.ts      |  32 +-
 src/client/util/InteractionUtils.tsx               |   1 +
 src/client/util/LinkFollower.ts                    |  34 +--
 src/client/util/LinkManager.ts                     |  32 +-
 src/client/util/ReplayMovements.ts                 |   4 +-
 src/client/util/SearchUtil.ts                      |  94 +++---
 src/client/util/SettingsManager.tsx                |   4 +-
 src/client/util/SharingManager.tsx                 |   4 +-
 src/client/util/SnappingManager.ts                 |   4 +-
 src/client/util/TrackMovements.ts                  |   2 +-
 src/client/views/AudioWaveform.tsx                 | 124 ++++----
 src/client/views/DashboardView.tsx                 |  18 +-
 src/client/views/DocComponent.tsx                  |  21 +-
 src/client/views/DocumentButtonBar.tsx             |  22 +-
 src/client/views/DocumentDecorations.tsx           |  70 ++---
 src/client/views/FilterPanel.tsx                   |   6 +-
 src/client/views/GestureOverlay.tsx                |  14 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 src/client/views/InkStrokeProperties.ts            |  14 +-
 src/client/views/InkTranscription.tsx              |   2 +-
 src/client/views/InkingStroke.tsx                  |  30 +-
 src/client/views/LightboxView.tsx                  |  44 +--
 src/client/views/MainView.tsx                      |  32 +-
 src/client/views/MarqueeAnnotator.tsx              |  12 +-
 src/client/views/PreviewCursor.tsx                 |   2 +-
 src/client/views/PropertiesButtons.tsx             |  36 +--
 .../views/PropertiesDocBacklinksSelector.tsx       |   2 +-
 src/client/views/PropertiesDocContextSelector.tsx  |  10 +-
 src/client/views/PropertiesView.tsx                |  78 ++---
 src/client/views/SidebarAnnos.tsx                  |  20 +-
 src/client/views/StyleProvider.tsx                 |  38 +--
 src/client/views/TemplateMenu.tsx                  |   4 +-
 src/client/views/animationtimeline/Timeline.tsx    |  14 +-
 src/client/views/animationtimeline/Track.tsx       | 261 +++++++++--------
 .../views/collections/CollectionCarouselView.tsx   |  14 +-
 .../views/collections/CollectionDockingView.tsx    |  22 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |  44 +--
 src/client/views/collections/CollectionMenu.tsx    |  85 +++---
 .../views/collections/CollectionNoteTakingView.tsx |  78 ++---
 .../collections/CollectionNoteTakingViewColumn.tsx |  23 +-
 .../views/collections/CollectionPileView.tsx       |   6 +-
 .../collections/CollectionStackedTimeline.tsx      |  39 +--
 .../views/collections/CollectionStackingView.tsx   |  82 +++---
 .../CollectionStackingViewFieldColumn.tsx          |  30 +-
 src/client/views/collections/CollectionSubView.tsx |  14 +-
 .../views/collections/CollectionTimeView.tsx       |   8 +-
 .../views/collections/CollectionTreeView.tsx       |  28 +-
 src/client/views/collections/CollectionView.tsx    |  16 +-
 src/client/views/collections/TabDocView.tsx        |  34 +--
 src/client/views/collections/TreeView.tsx          |  64 ++--
 .../CollectionFreeFormLinkView.tsx                 |  62 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 186 ++++++------
 .../collections/collectionFreeForm/MarqueeView.tsx |  32 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  34 +--
 .../collections/collectionSchema/SchemaRowBox.tsx  |   2 +-
 .../collectionSchema/SchemaTableCell.tsx           |   4 +-
 src/client/views/linking/LinkMenuGroup.tsx         |  21 +-
 src/client/views/linking/LinkMenuItem.tsx          |  30 +-
 .../views/linking/LinkRelationshipSearch.tsx       |  52 ++--
 src/client/views/nodes/AudioBox.tsx                |  37 +--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  16 +-
 src/client/views/nodes/ColorBox.tsx                |   4 +-
 src/client/views/nodes/ComparisonBox.tsx           |  28 +-
 src/client/views/nodes/DataVizBox/DataVizBox.tsx   |  18 +-
 .../nodes/DataVizBox/components/LineChart.tsx      |  14 +-
 .../views/nodes/DataVizBox/components/TableBox.tsx |  12 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  12 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   6 +-
 src/client/views/nodes/DocumentView.tsx            | 145 ++++-----
 src/client/views/nodes/EquationBox.tsx             |   2 +-
 src/client/views/nodes/FieldView.tsx               |   2 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |   2 +-
 src/client/views/nodes/ImageBox.tsx                |  90 +++---
 src/client/views/nodes/KeyValueBox.tsx             |  32 +-
 src/client/views/nodes/KeyValuePair.scss           |   4 +
 src/client/views/nodes/KeyValuePair.tsx            |   6 +-
 src/client/views/nodes/LabelBox.tsx                |   8 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  16 +-
 src/client/views/nodes/LinkBox.tsx                 |   4 +-
 src/client/views/nodes/LinkDescriptionPopup.tsx    |   2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  20 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  42 +--
 src/client/views/nodes/MapBox/MapBox2.tsx          |  42 +--
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |   6 +-
 src/client/views/nodes/PDFBox.tsx                  |  72 ++---
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   2 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  10 +-
 src/client/views/nodes/ScriptingBox.tsx            |  10 +-
 src/client/views/nodes/VideoBox.tsx                | 102 +++----
 src/client/views/nodes/WebBox.tsx                  | 123 ++++----
 src/client/views/nodes/button/FontIconBox.tsx      | 245 ++++++++--------
 .../views/nodes/formattedText/DashDocView.tsx      |  45 +--
 .../views/nodes/formattedText/DashFieldView.tsx    |  13 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 223 +++++++-------
 .../views/nodes/formattedText/RichTextMenu.tsx     |  14 +-
 .../views/nodes/formattedText/RichTextRules.ts     |  28 +-
 src/client/views/nodes/formattedText/nodes_rts.ts  |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          | 101 ++++---
 src/client/views/nodes/trails/PresElementBox.tsx   |   8 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |   4 +-
 src/client/views/pdf/PDFViewer.tsx                 |  56 ++--
 src/client/views/search/SearchBox.tsx              |  44 +--
 src/fields/Doc.ts                                  | 117 ++++----
 src/fields/RichTextUtils.ts                        |   6 +-
 src/fields/Types.ts                                |   5 +-
 src/fields/documentSchemas.ts                      |  52 ++--
 src/fields/util.ts                                 |   4 +-
 src/mobile/AudioUpload.tsx                         |  10 +-
 src/server/ApiManagers/SearchManager.ts            | 151 +++++-----
 src/server/updateProtos.ts                         |  24 +-
 src/server/websocket.ts                            |   4 +-
 123 files changed, 2424 insertions(+), 2391 deletions(-)

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

diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index ff9460b62..e8fd8fb8a 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,27 +1,26 @@
-import { AssertionError } from "assert";
-import { EditorState } from "prosemirror-state";
-import { Doc, DocListCastAsync, Opt } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { RichTextField } from "../../../fields/RichTextField";
-import { RichTextUtils } from "../../../fields/RichTextUtils";
-import { Cast, StrCast } from "../../../fields/Types";
-import { ImageField } from "../../../fields/URLField";
-import { MediaItem, NewMediaItemResult } from "../../../server/apis/google/SharedTypes";
-import { Utils } from "../../../Utils";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
-import { Networking } from "../../Network";
-import { FormattedTextBox } from "../../views/nodes/formattedText/FormattedTextBox";
-import { GoogleAuthenticationManager } from "../GoogleAuthenticationManager";
+import { AssertionError } from 'assert';
+import { EditorState } from 'prosemirror-state';
+import { Doc, DocListCastAsync, Opt } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { RichTextField } from '../../../fields/RichTextField';
+import { RichTextUtils } from '../../../fields/RichTextUtils';
+import { Cast, StrCast } from '../../../fields/Types';
+import { ImageField } from '../../../fields/URLField';
+import { MediaItem, NewMediaItemResult } from '../../../server/apis/google/SharedTypes';
+import { Utils } from '../../../Utils';
+import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
+import { Networking } from '../../Network';
+import { FormattedTextBox } from '../../views/nodes/formattedText/FormattedTextBox';
+import { GoogleAuthenticationManager } from '../GoogleAuthenticationManager';
 import Photos = require('googlephotos');
 
 export namespace GooglePhotos {
-
     const endpoint = async () => new Photos(await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken());
 
     export enum MediaType {
         ALL_MEDIA = 'ALL_MEDIA',
         PHOTO = 'PHOTO',
-        VIDEO = 'VIDEO'
+        VIDEO = 'VIDEO',
     }
 
     export type AlbumReference = { id: string } | { title: string };
@@ -58,11 +57,10 @@ export namespace GooglePhotos {
         HOUSES: 'HOUSES',
         GARDENS: 'GARDENS',
         FLOWERS: 'FLOWERS',
-        HOLIDAYS: 'HOLIDAYS'
+        HOLIDAYS: 'HOLIDAYS',
     };
 
     export namespace Export {
-
         export interface AlbumCreationResult {
             albumId: string;
             mediaItems: MediaItem[];
@@ -82,20 +80,20 @@ export namespace GooglePhotos {
             if (!images || !images.length) {
                 return undefined;
             }
-            const resolved = title ? title : (StrCast(collection.title) || `Dash Collection (${collection[Id]}`);
+            const resolved = title ? title : StrCast(collection.title) || `Dash Collection (${collection[Id]}`;
             const { id, productUrl } = await Create.Album(resolved);
             const response = await Transactions.UploadImages(images, { id }, descriptionKey);
             if (response) {
                 const { results, failed } = response;
                 let index: Opt<number>;
                 while ((index = failed.pop()) !== undefined) {
-                    Doc.RemoveDocFromList(dataDocument, "data", images.splice(index, 1)[0]);
+                    Doc.RemoveDocFromList(dataDocument, 'data', images.splice(index, 1)[0]);
                 }
                 const mediaItems: MediaItem[] = results.map(item => item.mediaItem);
                 if (mediaItems.length !== images.length) {
                     throw new AssertionError({ actual: mediaItems.length, expected: images.length });
                 }
-                const idMapping = new Doc;
+                const idMapping = new Doc();
                 for (let i = 0; i < images.length; i++) {
                     const image = Doc.GetProto(images[i]);
                     const mediaItem = mediaItems[i];
@@ -117,38 +115,30 @@ export namespace GooglePhotos {
                 return { albumId: id, mediaItems };
             }
         };
-
     }
 
     export namespace Import {
-
         export type CollectionConstructor = (data: Array<Doc>, options: DocumentOptions, ...args: any) => Doc;
 
         export const CollectionFromSearch = async (constructor: CollectionConstructor, requested: Opt<Partial<Query.SearchOptions>>): Promise<Doc> => {
             await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
             const response = await Query.ContentSearch(requested);
             const uploads = await Transactions.WriteMediaItemsToServer(response);
-            const children = uploads.map((upload: Transactions.UploadInformation) => {
-                const document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
-                document.contentSize = upload.contentSize;
-                return document;
-            });
+            const children = uploads.map((upload: Transactions.UploadInformation) => Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean) /*, {"data_contentSize":upload.contentSize}*/));
             const options = { _width: 500, _height: 500 };
             return constructor(children, options);
         };
-
     }
 
     export namespace Query {
-
-        const delimiter = ", ";
-        const comparator = (a: string, b: string) => (a < b) ? -1 : (a > b ? 1 : 0);
+        const delimiter = ', ';
+        const comparator = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0);
 
         export const TagChildImages = async (collection: Doc) => {
             await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
             const idMapping = await Cast(collection.googlePhotosIdMapping, Doc);
             if (!idMapping) {
-                throw new Error("Appending image metadata requires that the targeted collection have already been mapped to an album!");
+                throw new Error('Appending image metadata requires that the targeted collection have already been mapped to an album!');
             }
             const tagMapping = new Map<string, string>();
             const images = (await DocListCastAsync(collection.data))!.map(Doc.GetProto);
@@ -169,7 +159,7 @@ export namespace GooglePhotos {
                 const concatenated = tagMapping.get(image[Id])!;
                 const tags = concatenated.split(delimiter);
                 if (tags.length > 1) {
-                    const cleaned = concatenated.replace(ContentCategories.NONE + delimiter, "");
+                    const cleaned = concatenated.replace(ContentCategories.NONE + delimiter, '');
                     image.googlePhotosTags = cleaned.split(delimiter).sort(comparator).join(delimiter);
                 } else {
                     image.googlePhotosTags = ContentCategories.NONE;
@@ -253,19 +243,15 @@ export namespace GooglePhotos {
         export const GetImage = async (mediaItemId: string): Promise<Transactions.MediaItem> => {
             return (await endpoint()).mediaItems.get(mediaItemId);
         };
-
     }
 
     namespace Create {
-
         export const Album = async (title: string) => {
             return (await endpoint()).albums.create(title);
         };
-
     }
 
     export namespace Transactions {
-
         export interface UploadInformation {
             mediaPaths: string[];
             fileNames: { [key: string]: string };
@@ -295,18 +281,20 @@ export namespace GooglePhotos {
         };
 
         export const WriteMediaItemsToServer = async (body: { mediaItems: any[] }): Promise<UploadInformation[]> => {
-            const uploads = await Networking.PostToServer("/googlePhotosMediaGet", body);
+            const uploads = await Networking.PostToServer('/googlePhotosMediaGet', body);
             return uploads;
         };
 
-        export const UploadThenFetch = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption") => {
+        export const UploadThenFetch = async (sources: Doc[], album?: AlbumReference, descriptionKey = 'caption') => {
             const response = await UploadImages(sources, album, descriptionKey);
             if (!response) {
                 return undefined;
             }
-            const baseUrls: string[] = await Promise.all(response.results.map(item => {
-                return new Promise<string>(resolve => Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl)));
-            }));
+            const baseUrls: string[] = await Promise.all(
+                response.results.map(item => {
+                    return new Promise<string>(resolve => Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl)));
+                })
+            );
             return baseUrls;
         };
 
@@ -315,9 +303,9 @@ export namespace GooglePhotos {
             failed: number[];
         }
 
-        export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption"): Promise<Opt<ImageUploadResults>> => {
+        export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = 'caption'): Promise<Opt<ImageUploadResults>> => {
             await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
-            if (album && "title" in album) {
+            if (album && 'title' in album) {
                 album = await Create.Album(album.title);
             }
             const media: MediaInput[] = [];
@@ -327,13 +315,13 @@ export namespace GooglePhotos {
                     return;
                 }
                 const url = data.url.href;
-                const target = Doc.MakeAlias(source);
+                const target = Doc.MakeEmbedding(source);
                 const description = parseDescription(target, descriptionKey);
                 await DocUtils.makeCustomViewClicked(target, Docs.Create.FreeformDocument);
                 media.push({ url, description });
             }
             if (media.length) {
-                const results = await Networking.PostToServer("/googlePhotosMediaPost", { media, album });
+                const results = await Networking.PostToServer('/googlePhotosMediaPost', { media, album });
                 return results;
             }
         };
@@ -341,14 +329,12 @@ export namespace GooglePhotos {
         const parseDescription = (document: Doc, descriptionKey: string) => {
             let description: string = Utils.prepend(`/doc/${document[Id]}?sharing=true`);
             const target = document[descriptionKey];
-            if (typeof target === "string") {
+            if (typeof target === 'string') {
                 description = target;
             } else if (target instanceof RichTextField) {
                 description = RichTextUtils.ToPlainText(EditorState.fromJSON(FormattedTextBox.Instance.config, JSON.parse(target.Data)));
             }
             return description;
         };
-
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index d99cd2dac..2da3a24fd 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -11,7 +11,7 @@ export enum DocumentType {
     AUDIO = 'audio',
     REC = 'recording',
     PDF = 'pdf',
-    INK = 'inks',
+    INK = 'ink',
     SCREENSHOT = 'screenshot',
     FONTICON = 'fonticonbox',
     FILTER = 'filter',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e28e29267..849a5d6ae 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -37,7 +37,6 @@ import { FontIconBox } from '../views/nodes/button/FontIconBox';
 import { ColorBox } from '../views/nodes/ColorBox';
 import { ComparisonBox } from '../views/nodes/ComparisonBox';
 import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
-import { OpenWhereMod } from '../views/nodes/DocumentView';
 import { EquationBox } from '../views/nodes/EquationBox';
 import { FieldViewProps } from '../views/nodes/FieldView';
 import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
@@ -120,7 +119,7 @@ class PEInfo extends FInfo {
 }
 class DAInfo extends FInfo {
     fieldType? = 'dropActionType';
-    values? = ['alias', 'copy', 'move', 'same', 'proto', 'none'];
+    values? = ['embed', 'copy', 'move', 'same', 'proto', 'none'];
     readOnly = true;
 }
 class DateInfo extends FInfo {
@@ -139,7 +138,7 @@ export class DocumentOptions {
     x?: NUMt = new NumInfo('x coordinate of document in a freeform view');
     y?: NUMt = new NumInfo('y coordinage of document in a freeform view');
     z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, [1, 0]);
-    system?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
+    isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
     type?: STRt = new StrInfo('type of document', true, Array.from(Object.keys(DocumentType)));
     title?: string;
     creationDate?: DATEt = new DateInfo('date the document was created', true);
@@ -150,33 +149,33 @@ export class DocumentOptions {
     userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
     color?: STRt = new StrInfo('foreground color data doc');
     backgroundColor?: STRt = new StrInfo('background color for data doc');
-    _autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
+    _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
     _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
     _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
     _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
-    _panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
-    _panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
+    _freeform_panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
+    _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
     _width?: NUMt = new NumInfo('displayed width of a document');
     _height?: NUMt = new NumInfo('displayed height of document');
-    'data-nativeWidth'?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)');
-    'data-nativeHeight'?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)');
+    data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)');
+    data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)');
     _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)');
     _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)');
     _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
     _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
-    _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
-    _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
+    _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
+    _layoutFitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
     _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
-    _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
+    _lockedTransform?: boolean; // lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed
     _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target
-    _showTitle?: string; // field name to display in header (:hover is an optional suffix)
-    _showAltContentUI?: boolean; // whether to show alternate content button
+    _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
+    _layout_altContentUI?: boolean; // whether to show alternate content button
     _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target
-    _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
-    _scrollTop?: number; // scroll location for pdfs
+    _layout_showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
+    _layoutScrollTop?: number; // scroll location for pdfs
     _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged
     _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
     _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
@@ -188,15 +187,16 @@ export class DocumentOptions {
     _viewType?: string; // sub type of a collection
     viewType?: string; // sub type of a collection
     _gridGap?: number; // gap between items in masonry view
-    _viewScale?: number; // how much a freeform view has been scaled (zoomed)
+    _freeform_scale?: number; // how much a freeform view has been scaled (zoomed)
     _overflow?: string; // set overflow behavior
     _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts
     _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts
     _xPadding?: number;
     _yPadding?: number;
     _itemIndex?: number; // which item index the carousel viewer is showing
-    _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts
-    _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document)
+    _layout_showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts
+    _singleLine?: boolean; // whether label box is restricted to one line of text
+    _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents
     _minFontSize?: number; // minimum font size for labelBoxes
     _maxFontSize?: number; // maximum font size for labelBoxes
     _columnWidth?: number;
@@ -205,24 +205,24 @@ export class DocumentOptions {
     _fontFamily?: string;
     _fontWeight?: string;
     _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
-    _curPage?: number; // current page of a PDF or other? paginated document
-    _currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video)  value is in seconds
+    _layout_curPage?: number; // current page of a PDF or other? paginated document
+    _layout_currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video)  value is in seconds
     _currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide)
     _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
     _timecodeToHide?: number; // the time that a document should be hidden
     _timelineLabel?: boolean; // whether the document exists on a timeline
-    '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection', true);
-    '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection', true);
-    'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view', true);
-    'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view', true);
-    'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
+    _caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', true);
+    _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', true);
+    icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', true);
+    icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', true);
+    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
     openFactoryAsDelegate?: boolean; //
     lat?: number;
     lng?: number;
     infoWindowOpen?: boolean;
     author?: string;
-    _layoutKey?: string;
+    _layout_fieldKey?: string;
     fieldValues?: List<any>; // possible field values used by fieldInfos
     fieldType?: string; // type of afield used by fieldInfos
     unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)
@@ -249,7 +249,6 @@ export class DocumentOptions {
     defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
     waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
-    description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
     contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
@@ -263,11 +262,11 @@ export class DocumentOptions {
     childContextMenuLabels?: List<string>;
     childContextMenuIcons?: List<string>;
     followLinkZoom?: boolean; // whether to zoom to the target of a link
-    hideLinkButton?: boolean; // whether the blue link counter button should be hidden
+    layout_hideLinkButton?: boolean; // whether the blue link counter button should be hidden
     disableDocBrushing?: boolean; // whether to suppress border highlighting
-    hideDecorationTitle?: boolean;
+    layout_hideDecorationTitle?: boolean;
     hideOpenButton?: boolean;
-    hideResizeHandles?: boolean;
+    layout_hideResizeHandles?: boolean;
     hideDocumentButtonBar?: boolean;
     hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
@@ -278,8 +277,8 @@ export class DocumentOptions {
     caption?: RichTextField;
     opacity?: number;
     defaultBackgroundColor?: string;
-    _linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint
-    hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed
+    _layout_autoMoveAnchors?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint
+    layout_hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed
     isFolder?: boolean;
     lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide)
     activeFrame?: number; // the active frame of a document in a frame base collection
@@ -294,12 +293,11 @@ export class DocumentOptions {
     borderRounding?: string;
     boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
     data?: any;
-    baseProto?: boolean; // is this a base prototoype
+    isBaseProto?: boolean; // this Doc is base level prototype for data documents as opposed to data documents which are prototypes for layout documents.  base protos are not cloned during a deep
     dontRegisterView?: boolean;
     lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document.  see PresBox.
     columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
     schemaHeaders?: List<SchemaHeaderField>; // headers for schema view
-    clipWidth?: number; // percent transition from before to after in comparisonBox
     dockingConfig?: string;
     annotationOn?: Doc;
     followLinkToggle?: boolean;
@@ -307,7 +305,7 @@ export class DocumentOptions {
     _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
     noteType?: string;
     // BACKGROUND GRID
-    _backgroundGridShow?: boolean;
+    _freeform_backgroundGrid?: boolean;
 
     //BUTTONS
     buttonText?: string;
@@ -318,7 +316,6 @@ export class DocumentOptions {
     userColorBtn?: string;
     canClick?: string;
     script?: ScriptField;
-    numBtnType?: string;
     numBtnMax?: number;
     numBtnMin?: number;
     switchToggle?: boolean;
@@ -335,12 +332,14 @@ export class DocumentOptions {
 
     layout_linkView?: Doc; // view template for a link document
     layout_keyValue?: string; // view tempalte for key value docs
-    linkRelationship?: string; // type of relatinoship a link represents
-    linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors
-    anchor1?: Doc;
-    anchor2?: Doc;
-    'anchor1-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
-    'anchor2-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    link_description?: string; // added for links
+    link_relationship?: string; // type of relatinoship a link represents
+    layout_linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors
+    layout_linkDisplayArrow?: boolean; // whether to display link's directional arrowhead
+    link_anchor_1?: Doc;
+    link_anchor_2?: Doc;
+    link_anchor_1_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    link_anchor_2_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
     ignoreClick?: boolean;
     onClick?: ScriptField;
     onDoubleClick?: ScriptField;
@@ -354,13 +353,13 @@ export class DocumentOptions {
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
     filterBoolean?: string;
-    useCors?: boolean;
+    data_useCors?: boolean;
     icon?: string;
     target?: Doc; // available for use in scripts as the primary target document
     sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
     targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
     searchFileTypes?: List<string>; // file types allowed in a search query
-    strokeWidth?: number;
+    stroke_width?: number;
     freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection
     treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
     treeViewHideUnrendered?: boolean; // tells tree view not to display documents that have an 'unrendered' tag unless they also have a treeViewFieldKey tag (presBox)
@@ -395,8 +394,8 @@ export class DocumentOptions {
     useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox
     border?: string; //for searchbox
     hoverBackgroundColor?: string; // background color of a label when hovered
-    linkRelationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
-    linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship
+    link_relationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
+    link_relationshipSizes?: List<number>; //stores number of links contained in each relationship
     linkColorList?: List<string>; // colors of links corresponding to specific link relationships
 }
 export namespace Docs {
@@ -427,7 +426,7 @@ export namespace Docs {
                         _yMargin: 10,
                         nativeDimModifiable: true,
                         nativeHeightUnfrozen: true,
-                        forceReflow: true,
+                        layout_forceReflow: true,
                         defaultDoubleClick: 'ignore',
                     },
                 },
@@ -457,35 +456,35 @@ export namespace Docs {
                 DocumentType.WEB,
                 {
                     layout: { view: WebBox, dataField: defaultDataKey },
-                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
+                    options: { _height: 300, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
                 },
             ],
             [
                 DocumentType.COL,
                 {
                     layout: { view: CollectionView, dataField: defaultDataKey },
-                    options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1 },
+                    options: { _layout_fitWidth: true, _freeform_panX: 0, _freeform_panY: 0, _freeform_scale: 1 },
                 },
             ],
             [
                 DocumentType.KVP,
                 {
                     layout: { view: KeyValueBox, dataField: defaultDataKey },
-                    options: { _fitWidth: true, _height: 150 },
+                    options: { _layout_fitWidth: true, _height: 150 },
                 },
             ],
             [
                 DocumentType.VID,
                 {
                     layout: { view: VideoBox, dataField: defaultDataKey },
-                    options: { _currentTimecode: 0 },
+                    options: { _layout_currentTimecode: 0 },
                 },
             ],
             [
                 DocumentType.AUDIO,
                 {
                     layout: { view: AudioBox, dataField: defaultDataKey },
-                    options: { _height: 100, fitWidth: true, forceReflow: true, nativeDimModifiable: true },
+                    options: { _height: 100, layout_fitWidth: true, layout_forceReflow: true, nativeDimModifiable: true },
                 },
             ],
             [
@@ -499,7 +498,7 @@ export namespace Docs {
                 DocumentType.PDF,
                 {
                     layout: { view: PDFBox, dataField: defaultDataKey },
-                    options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
+                    options: { _layout_curPage: 1, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
                 },
             ],
             [
@@ -519,14 +518,15 @@ export namespace Docs {
             [
                 DocumentType.LINK,
                 {
-                    layout: { view: LinkBox, dataField: defaultDataKey },
+                    layout: { view: LinkBox, dataField: 'link' },
                     options: {
                         childDontRegisterViews: true,
                         onClick: FollowLinkScript(),
-                        hideLinkAnchors: true,
+                        layout_hideLinkAnchors: true,
                         _height: 150,
-                        description: '',
-                        showCaption: 'description',
+                        link: '',
+                        link_description: '',
+                        layout_showCaption: 'link_description',
                         backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
                         _removeDropProperties: new List(['onClick']),
                     },
@@ -537,7 +537,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'alias', title: 'Global Link Database' },
+                    options: { childDropAction: 'embed', title: 'Global Link Database' },
                 },
             ],
             [
@@ -545,7 +545,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'alias', title: 'Global Script Database' },
+                    options: { childDropAction: 'embed', title: 'Global Script Database' },
                 },
             ],
             [
@@ -571,8 +571,8 @@ export namespace Docs {
             [
                 DocumentType.EQUATION,
                 {
-                    layout: { view: EquationBox, dataField: defaultDataKey },
-                    options: { nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true },
+                    layout: { view: EquationBox, dataField: 'text' },
+                    options: { nativeDimModifiable: true, fontSize: '14px', layout_hideResizeHandles: true, layout_hideDecorationTitle: true },
                 },
             ],
             [
@@ -600,14 +600,14 @@ export namespace Docs {
                 DocumentType.PRES,
                 {
                     layout: { view: PresBox, dataField: defaultDataKey },
-                    options: { defaultDoubleClick: 'ignore', hideLinkAnchors: true },
+                    options: { defaultDoubleClick: 'ignore', layout_hideLinkAnchors: true },
                 },
             ],
             [
                 DocumentType.FONTICON,
                 {
                     layout: { view: FontIconBox, dataField: 'icon' },
-                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, hideLinkButton: true, _width: 40, _height: 40 },
+                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, layout_hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
             [
@@ -621,21 +621,21 @@ export namespace Docs {
                 DocumentType.PRESELEMENT,
                 {
                     layout: { view: PresElementBox, dataField: defaultDataKey },
-                    options: { title: 'pres element template', _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' },
+                    options: { title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' },
                 },
             ],
             [
                 DocumentType.MARKER,
                 {
                     layout: { view: CollectionView, dataField: defaultDataKey },
-                    options: { hideLinkButton: true, pointerEvents: 'none' },
+                    options: { layout_hideLinkButton: true, pointerEvents: 'none' },
                 },
             ],
             [
                 DocumentType.INK,
                 {
                     // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
-                    layout: { view: InkingStroke, dataField: defaultDataKey },
+                    layout: { view: InkingStroke, dataField: 'stroke' },
                     options: {},
                 },
             ],
@@ -649,8 +649,9 @@ export namespace Docs {
             [
                 DocumentType.COMPARISON,
                 {
+                    data: '',
                     layout: { view: ComparisonBox, dataField: defaultDataKey },
-                    options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias' },
+                    options: { nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'embed' },
                 },
             ],
             [
@@ -658,7 +659,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'alias', title: 'Global Group Database' },
+                    options: { childDropAction: 'embed', title: 'Global Group Database' },
                 },
             ],
             [
@@ -672,14 +673,14 @@ export namespace Docs {
                 DocumentType.DATAVIZ,
                 {
                     layout: { view: DataVizBox, dataField: defaultDataKey },
-                    options: { _fitWidth: true, nativeDimModifiable: true },
+                    options: { _layout_fitWidth: true, nativeDimModifiable: true },
                 },
             ],
             [
                 DocumentType.LOADING,
                 {
                     layout: { view: LoadingBox, dataField: '' },
-                    options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true },
+                    options: { _layout_fitWidth: true, _fitHeight: true, nativeDimModifiable: true },
                 },
             ],
         ]);
@@ -773,11 +774,11 @@ export namespace Docs {
             // synthesize the default options, the type and title from computed values and
             // whatever options pertain to this specific prototype
             const options: DocumentOptions = {
-                system: true,
-                _layoutKey: 'layout',
+                isSystem: true,
+                _layout_fieldKey: 'layout',
                 title,
                 type,
-                baseProto: true,
+                isBaseProto: true,
                 x: 0,
                 y: 0,
                 _width: 300,
@@ -818,24 +819,24 @@ export namespace Docs {
          * main document.
          */
         function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) {
-            const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_"
+            const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_"
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
             // dataProps['acl-Override'] = SharingPermissions.Unset;
             dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
-            dataProps.system = viewProps.system;
-            dataProps.isPrototype = true;
+            dataProps.isSystem = viewProps.isSystem;
+            dataProps.isDataDoc = true;
             dataProps.author = Doc.CurrentUserEmail;
             dataProps.creationDate = new DateField();
             if (fieldKey) {
-                dataProps[`${fieldKey}-lastModified`] = new DateField();
+                dataProps[`${fieldKey}_modificationDate`] = new DateField();
                 dataProps[fieldKey] = data;
 
                 // so that the list of annotations is already initialised, prevents issues in addonly.
                 // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
-                dataProps[fieldKey + '-annotations'] = new List<Doc>();
-                dataProps[fieldKey + '-sidebar'] = new List<Doc>();
+                dataProps[fieldKey + '_annotations'] = new List<Doc>();
+                dataProps[fieldKey + '_sidebar'] = new List<Doc>();
             }
 
             // users placeholderDoc as proto if it exists
@@ -901,7 +902,7 @@ export namespace Docs {
         }
 
         export function ComparisonDocument(options: DocumentOptions = { title: 'Comparison Box' }) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options);
+            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), undefined, options);
         }
 
         export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
@@ -954,11 +955,12 @@ export namespace Docs {
                 Prototypes.get(DocumentType.LINK),
                 undefined,
                 {
-                    anchor1: source,
-                    anchor2: target,
+                    link_anchor_1: source,
+                    link_anchor_2: target,
                     ...options,
                 },
-                id
+                id,
+                'link'
             );
 
             LinkManager.Instance.addLink(linkDoc);
@@ -966,36 +968,24 @@ export namespace Docs {
             return linkDoc;
         }
 
-        export function InkDocument(
-            color: string,
-            tool: string,
-            strokeWidth: number,
-            strokeBezier: string,
-            fillColor: string,
-            arrowStart: string,
-            arrowEnd: string,
-            dash: string,
-            points: PointData[],
-            isInkMask: boolean,
-            options: DocumentOptions = {}
-        ) {
+        export function InkDocument(color: string, strokeWidth: number, stroke_bezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], isInkMask: boolean, options: DocumentOptions = {}) {
             const I = new Doc();
             I[Initializing] = true;
-            I.isInkMask = isInkMask;
             I.type = DocumentType.INK;
-            I.layout = InkingStroke.LayoutString('data');
-            I.color = color;
-            I.hideDecorationTitle = true; // don't show title when selected
+            I.layout = InkingStroke.LayoutString('stroke');
+            I.layout_fitWidth = true;
+            I.layout_hideDecorationTitle = true; // don't show title when selected
             // I.hideOpenButton = true; // don't show open full screen button when selected
+            I.color = color;
             I.fillColor = fillColor;
-            I.strokeWidth = strokeWidth;
-            I.strokeBezier = strokeBezier;
-            I.strokeStartMarker = arrowStart;
-            I.strokeEndMarker = arrowEnd;
-            I.strokeDash = dash;
-            I.tool = tool;
-            I.fitWidth = true;
-            I['text-align'] = 'center';
+            I.stroke = new InkField(points);
+            I.stroke_width = strokeWidth;
+            I.stroke_bezier = stroke_bezier;
+            I.stroke_startMarker = arrowStart;
+            I.stroke_endMarker = arrowEnd;
+            I.stroke_dash = dash;
+            I.stroke_isInkMask = isInkMask;
+            I.text_align = 'center';
             I.title = 'ink';
             I.x = options.x as number;
             I.y = options.y as number;
@@ -1004,7 +994,6 @@ export namespace Docs {
             I._fontFamily = 'cursive';
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
-            I.data = new InkField(points);
             I.defaultDoubleClick = 'click';
             I.creationDate = new DateField();
             I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
@@ -1050,7 +1039,7 @@ export namespace Docs {
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id);
-            documents.map(d => (d.context = inst));
+            documents.map(d => (d.embedContainer = inst));
             return inst;
         }
 
@@ -1136,7 +1125,7 @@ export namespace Docs {
         }
 
         export function EquationDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.EQUATION), undefined, { ...(options || {}) });
+            return InstanceFromProto(Prototypes.get(DocumentType.EQUATION), undefined, { ...(options || {}) }, undefined, 'text');
         }
 
         export function FunctionPlotDocument(documents: Array<Doc>, options?: DocumentOptions) {
@@ -1263,7 +1252,7 @@ export namespace DocUtils {
                           ? true
                           : matches.some(value => {
                                 if (facetKey.startsWith('*')) {
-                                    //  fields starting with a '*' are used to match families of related fields.  ie, *lastModified will match text-lastModified, data-lastModified, etc
+                                    //  fields starting with a '*' are used to match families of related fields.  ie, *modificationDate will match text_modificationDate, data_modificationDate, etc
                                     const allKeys = Array.from(Object.keys(d));
                                     allKeys.push(...Object.keys(Doc.GetProto(d)));
                                     const keys = allKeys.filter(key => key.includes(facetKey.substring(1)));
@@ -1304,12 +1293,12 @@ export namespace DocUtils {
         broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
         return DocUtils.ActiveRecordings.map(audio => {
             const sourceDoc = getSourceDoc();
-            return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { linkDisplay: false, linkRelationship: 'recording annotation:linked recording', description: 'recording timeline' });
+            return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { layout_linkDisplay: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' });
         });
     }
 
-    export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, showPopup?: number[]) {
-        if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
+    export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; layout_linkDisplay?: boolean }, id?: string, showPopup?: number[]) {
+        if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
         const sv = DocumentManager.Instance.getDocumentView(source);
         if (target.doc === Doc.UserDoc()) return undefined;
 
@@ -1350,16 +1339,16 @@ export namespace DocUtils {
                 target,
                 {
                     title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
-                    'anchor1-useLinkSmallAnchor': source.useLinkSmallAnchor ? true : undefined,
-                    'anchor2-useLinkSmallAnchor': target.useLinkSmallAnchor ? true : undefined,
+                    link_anchor_1_useLinkSmallAnchor: source.useLinkSmallAnchor ? true : undefined,
+                    link_anchor_2_useLinkSmallAnchor: target.useLinkSmallAnchor ? true : undefined,
                     'acl-Public': SharingPermissions.Augment,
                     '_acl-Public': SharingPermissions.Augment,
-                    linkDisplay: linkSettings.linkDisplay,
-                    _linkAutoMove: true,
-                    linkRelationship: linkSettings.linkRelationship,
-                    _showCaption: 'description',
-                    _showTitle: 'linkRelationship',
-                    description: linkSettings.description,
+                    layout_linkDisplay: linkSettings.layout_linkDisplay,
+                    link_relationship: linkSettings.link_relationship,
+                    link_description: linkSettings.link_description,
+                    _layout_autoMoveAnchors: true,
+                    _layout_showCaption: 'link_description',
+                    _layout_showTitle: 'link_relationship',
                 },
                 id
             ),
@@ -1443,13 +1432,13 @@ export namespace DocUtils {
             created = Docs.Create.AudioDocument(field.url.href, resolved);
             created.layout = AudioBox.LayoutString(fieldKey);
         } else if (field instanceof InkField) {
-            created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved);
             created.layout = InkingStroke.LayoutString(fieldKey);
         } else if (field instanceof List && field[0] instanceof Doc) {
             created = Docs.Create.StackingDocument(DocListCast(field), resolved);
             created.layout = CollectionView.LayoutString(fieldKey);
         } else {
-            created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
+            created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved });
             created.layout = FormattedTextBox.LayoutString(fieldKey);
         }
         if (created) {
@@ -1503,12 +1492,12 @@ export namespace DocUtils {
                 const id = s[s.length - 1];
                 return DocServer.GetRefField(id).then(field => {
                     if (field instanceof Doc) {
-                        const alias = Doc.MakeAlias(field);
-                        alias.x = (options.x as number) || 0;
-                        alias.y = (options.y as number) || 0;
-                        alias._width = (options._width as number) || 300;
-                        alias._height = (options._height as number) || (options._width as number) || 300;
-                        return alias;
+                        const embedding = Doc.MakeEmbedding(field);
+                        embedding.x = (options.x as number) || 0;
+                        embedding.y = (options.y as number) || 0;
+                        embedding._width = (options._width as number) || 300;
+                        embedding._height = (options._height as number) || (options._width as number) || 300;
+                        return embedding;
                     }
                     return undefined;
                 });
@@ -1531,11 +1520,11 @@ export namespace DocUtils {
                             _width: 200,
                             x,
                             y,
-                            _autoHeight: note._autoHeight !== false,
-                            title: StrCast(note.title) + '#' + (note.aliasCount = NumCast(note.aliasCount) + 1),
+                            _layout_autoHeight: note._layout_autoHeight !== false,
+                            title: StrCast(note.title) + '#' + (note.embeddingCount = NumCast(note.embeddingCount) + 1),
                         });
-                        textDoc.layoutKey = 'layout_' + note.title;
-                        textDoc[textDoc.layoutKey] = note;
+                        textDoc.layout_fieldKey = 'layout_' + note.title;
+                        textDoc[textDoc.layout_fieldKey] = note;
                         if (pivotField) {
                             textDoc[pivotField] = pivotValue;
                         }
@@ -1576,7 +1565,7 @@ export namespace DocUtils {
     export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
         const batch = UndoManager.StartBatch('makeCustomViewClicked');
         runInAction(() => {
-            doc.layoutKey = 'layout_' + templateSignature;
+            doc.layout_fieldKey = 'layout_' + templateSignature;
             createCustomView(doc, creator, templateSignature, docLayoutTemplate);
         });
         batch.end();
@@ -1607,7 +1596,7 @@ export namespace DocUtils {
         const customName = 'layout_' + templateSignature;
         const _width = NumCast(doc._width);
         const _height = NumCast(doc._height);
-        const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _showSidebar: false };
+        const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _layout_autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _layout_showSidebar: false };
 
         if (docLayoutTemplate) {
             if (docLayoutTemplate !== doc[customName]) {
@@ -1638,9 +1627,9 @@ export namespace DocUtils {
         }
     }
     export function iconify(doc: Doc) {
-        const layoutKey = Cast(doc.layoutKey, 'string', null);
+        const layout_fieldKey = Cast(doc.layout_fieldKey, 'string', null);
         DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined);
-        if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') doc.deiconifyLayout = layoutKey.replace('layout_', '');
+        if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') doc.deiconifyLayout = layout_fieldKey.replace('layout_', '');
     }
 
     export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
@@ -1680,8 +1669,8 @@ export namespace DocUtils {
 
     export function LeavePushpin(doc: Doc, annotationField: string) {
         if (doc.followLinkToggle) return undefined;
-        const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null);
-        const hasContextAnchor = LinkManager.Links(doc).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context));
+        const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null);
+        const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context));
         if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
             const pushpin = Docs.Create.FontIconDocument({
                 title: 'pushpin',
@@ -1700,7 +1689,7 @@ export namespace DocUtils {
                 _timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
             });
             Doc.AddDocToList(context, annotationField, pushpin);
-            const pushpinLink = DocUtils.MakeLink(pushpin, doc, { linkRelationship: 'pushpin' }, '');
+            const pushpinLink = DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, '');
             doc._timecodeToShow = undefined;
             return pushpin;
         }
@@ -1745,21 +1734,17 @@ export namespace DocUtils {
         if (doc) {
             const proto = Doc.GetProto(doc);
             proto.text = result.rawText;
-            proto.fileUpload = pathname
-                .replace(/.*\//, '')
-                .replace('upload_', '')
-                .replace(/\.[a-z0-9]*$/, '');
             if (Upload.isImageInformation(result)) {
                 const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
                 const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
                 proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
-                proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
-                proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
                 if (NumCast(proto['data-nativeOrientation']) >= 5) {
-                    proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
-                    proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                    proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                    proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
                 }
-                proto.contentSize = result.contentSize;
+                proto.data_contentSize = result.contentSize;
                 // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
                 const latitude = result.exifData?.data?.GPSLatitude;
                 const latitudeDirection = result.exifData?.data?.GPSLatitudeRef;
@@ -1771,12 +1756,12 @@ export namespace DocUtils {
                 }
             }
             if (Upload.isVideoInformation(result)) {
-                proto['data-duration'] = result.duration;
+                proto.data_duration = result.duration;
             }
             if (overwriteDoc) {
                 Doc.removeCurrentlyLoading(overwriteDoc);
                 // loading doc icons are just labels. so any icon views of loading docs need to be replaced with the proper icon view.
-                DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layoutKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
+                DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
             }
             generatedDocuments.push(doc);
         }
@@ -1793,16 +1778,16 @@ export namespace DocUtils {
             _height: 35,
             x: x,
             y: y,
-            _fitWidth: true,
-            _autoHeight: true,
-            _showAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
+            _layout_fitWidth: true,
+            _layout_autoHeight: true,
+            _layout_altContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
             title,
         });
         const template = Doc.UserDoc().defaultTextLayout;
         if (template instanceof Doc) {
             tbox._width = NumCast(template._width);
-            tbox.layoutKey = 'layout_' + StrCast(template.title);
-            Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
+            tbox.layout_fieldKey = 'layout_' + StrCast(template.title);
+            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template;
         }
         return tbox;
     }
@@ -1857,9 +1842,9 @@ export namespace DocUtils {
         if (!dragFactory) return undefined;
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
         ndoc && Doc.AddFileOrphan(Doc.GetProto(ndoc));
-        if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
-            dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
-            Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(), true);
+        if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
+            dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
+            Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
         }
 
         if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc);
@@ -1868,9 +1853,9 @@ export namespace DocUtils {
     }
     export function delegateDragFactory(dragFactory: Doc) {
         const ndoc = Doc.MakeDelegateWithProto(dragFactory);
-        if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
-            dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
-            Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString();
+        if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
+            dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
+            Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString();
         }
         return ndoc;
     }
@@ -1885,11 +1870,8 @@ ScriptingGlobals.add(function makeDelegate(proto: any) {
     return d;
 });
 ScriptingGlobals.add(function generateLinkTitle(self: Doc) {
-    const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null)?.title : '<?>';
-    const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null)?.title : '<?>';
-    const relation = self.linkRelationship || 'to';
-    return `${anchor1title} (${relation}) ${anchor2title}`;
-});
-ScriptingGlobals.add(function openTabAlias(tab: Doc) {
-    CollectionDockingView.AddSplit(Doc.MakeAlias(tab), OpenWhereMod.right);
+    const link_anchor_1title = self.link_anchor_1 && self.link_anchor_1 !== self ? Cast(self.link_anchor_1, Doc, null)?.title : '<?>';
+    const link_anchor_2title = self.link_anchor_2 && self.link_anchor_2 !== self ? Cast(self.link_anchor_2, Doc, null)?.title : '<?>';
+    const relation = self.link_relationship || 'to';
+    return `${link_anchor_1title} (${relation}) ${link_anchor_2title}`;
 });
diff --git a/src/client/documents/Gitlike.ts b/src/client/documents/Gitlike.ts
index 5e2baf924..f35f33f65 100644
--- a/src/client/documents/Gitlike.ts
+++ b/src/client/documents/Gitlike.ts
@@ -9,24 +9,24 @@
 // // we don't have individual timestamps for all fields -- this is a problematic design issue.
 // function GitlikeSynchDocs(bd: Doc, md: Doc) {
 //     const fieldKey = Doc.LayoutFieldKey(md);
-//     const bdate = DateCast(bd[`${fieldKey}-lastModified`])?.date;
-//     const mdate = DateCast(md[`${fieldKey}-lastModified`])?.date;
+//     const bdate = DateCast(bd[`${fieldKey}_modificationDate`])?.date;
+//     const mdate = DateCast(md[`${fieldKey}_modificationDate`])?.date;
 //     const bdproto = bd && Doc.GetProto(bd);
 //     if (bdate !== mdate && bdate <= mdate) {
 //         if (bdproto && md) {
 //             bdproto[fieldKey] = Field.Copy(md[fieldKey]);
-//             bdproto[`${fieldKey}-lastModified`] = new DateField();
+//             bdproto[`${fieldKey}_modificationDate`] = new DateField();
 //         }
 //     }
-//     const bldate = DateCast(bd._lastModified)?.date;
-//     const mldate = DateCast(md._lastModified)?.date;
+//     const bldate = DateCast(bd._layout_modificationDate)?.date;
+//     const mldate = DateCast(md._layout_modificationDate)?.date;
 //     if (bldate === mldate || bldate > mldate) return;
 //     if (bdproto && md) {
 //         bd.x = Field.Copy(md.x);
 //         bd.y = Field.Copy(md.y);
 //         bd.width = Field.Copy(md.width);
 //         bd.height = Field.Copy(md.height);
-//         bdproto._lastModified = new DateField();
+//         bdproto._layout_modificationDate = new DateField();
 //     }
 // }
 
@@ -53,10 +53,10 @@
 //     const newlyBranchedDocs = await Promise.all(newDocsFromMaster?.map(async md => (await Doc.MakeClone(md, false, true, cloneMap)).clone) || []);
 //     newlyBranchedDocs.forEach(nd => {
 //         Doc.AddDocToList(branch, Doc.LayoutFieldKey(branch) + suffix, nd);
-//         nd.context = branch;
+//         nd.embedContainer = branch;
 //     });
-//     // if a branch doc's corresponding main branch doc doesn't have a context, then it was deleted.
-//     const remDocsFromMaster = branchMainDocs?.filter(bd => Cast(bd.branchOf, Doc, null) && !Cast(bd.branchOf, Doc, null)?.context);
+//     // if a branch doc's corresponding main branch doc doesn't have a embedContainer, then it was deleted.
+//     const remDocsFromMaster = branchMainDocs?.filter(bd => Cast(bd.branchOf, Doc, null) && !Cast(bd.branchOf, Doc, null)?.embedContainer);
 //     // so then remove all the deleted main docs from this branch.
 //     remDocsFromMaster?.forEach(rd => Doc.RemoveDocFromList(branch, Doc.LayoutFieldKey(branch) + suffix, rd));
 // }
@@ -87,14 +87,14 @@
 //                 branchDocProto.branchOf = masterChild;                                 // the branch child is now a branch of the master child
 //             }
 //             Doc.AddDocToList(master, Doc.LayoutFieldKey(master) + suffix, masterChild); // add the masterChild to master (if it's already there, this is a no-op)
-//             masterChild.context = master;
+//             masterChild.embedContainer = master;
 //             GitlikeSynchDocs(masterChild, bd);//Doc.GetProto(masterChild), bd);
 //         }));
 //         const masterChildren = await DocListCastAsync(master[Doc.LayoutFieldKey(master) + suffix]);
 //         masterChildren?.forEach(mc => {                      // see if any master children
 //             if (!branchChildren?.find(bc => bc.branchOf === mc)) { //    are not in the list of children for this branch.
 //                 Doc.RemoveDocFromList(master, Doc.LayoutFieldKey(master) + suffix, mc); // if so, delete the master child since the branch has deleted it.
-//                 mc.context = undefined;      // NOTE if we merge a branch that didn't do a pull, it will look like the branch deleted documents -- need edit timestamps that prevent merging if branch isn't up-to-date with last edit timestamp
+//                 mc.embedContainer = undefined;      // NOTE if we merge a branch that didn't do a pull, it will look like the branch deleted documents -- need edit timestamps that prevent merging if branch isn't up-to-date with last edit timestamp
 //             }
 //         });
 //     });
@@ -109,8 +109,8 @@
 // export async function BranchTask(target: Doc, action: "pull" | "merge") {
 //     const func = action === "pull" ? GitlikePullFromMaster : GitlikeMergeWithMaster;
 //     await func(target, "");
-//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "-annotations"));
-//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "-sidebar"));
+//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "_annotations"));
+//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "_sidebar"));
 // }
 
 // export async function BranchCreate(target: Doc) {
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index c9fcc84a3..d68761ba7 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -53,7 +53,7 @@ export class CaptureManager extends React.Component<{}> {
                 order.push(
                     <div className="list-item">
                         <div className="number">{i}</div>
-                        {StrCast(DocCast(l.anchor1)?.title)}
+                        {StrCast(DocCast(l.link_anchor_1)?.title)}
                     </div>
                 )
             );
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index fbfe306a4..5cfd77937 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -20,7 +20,7 @@ import { TreeViewType } from "../views/collections/CollectionTreeView";
 import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
-import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
+import { ButtonType } from "../views/nodes/button/FontIconBox";
 import { OpenWhere } from "../views/nodes/DocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DragManager } from "./DragManager";
@@ -37,7 +37,6 @@ interface Button {
     toolTip?: string;
     icon?: string;
     btnType?: ButtonType;
-    numBtnType?: NumButtonType;
     numBtnMin?: number;
     numBtnMax?: number;
     switchToggle?: boolean;
@@ -65,11 +64,11 @@ export class CurrentUserUtils {
         const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [
             {
                 btnOpts: { title: "slide", icon: "address-card" }, 
-                templateOpts: { _width: 400, _height: 300, title: "slideView",  _xMargin: 3, _yMargin: 3, system: true }, 
+                templateOpts: { _width: 400, _height: 300, title: "slideView",  _xMargin: 3, _yMargin: 3, isSystem: true }, 
                 template: (opts:DocumentOptions) => Docs.Create.MultirowDocument(
                     [
-                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200,  system: true }),
-                        Docs.Create.TextDocument("", { title: "text", _fitWidth:true,  _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
+                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200,  isSystem: true }),
+                        Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true,  _height: 100, isSystem: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
                     ], opts)
             },
             {
@@ -97,9 +96,9 @@ export class CurrentUserUtils {
         });
 
         const reqdOpts:DocumentOptions = {
-            title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
-            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
-            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
+            title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true,
+            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, isSystem: true,
+            _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
         const reqdFuncs = { hidden: "IsNoviceMode()" };
@@ -109,7 +108,7 @@ export class CurrentUserUtils {
     /// Initializes templates for editing click funcs of a document 
     static setupChildClickEditors(doc: Doc, field = "clickFuncs-child") {
         const tempClicks = DocCast(doc[field]);
-        const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, system: true};
+        const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
             { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCast(documentView?.props.docViewPath().lastElement()?.rootDoc.target).then((target) => target && (target.proto.data = new List([self])))"},
             { opts: { title: "Open Detail On Right",  targetScriptKey: "onChildDoubleClick"}, script: `openDoc(self.doubleClickView.${OpenWhere.addRight})`}];
@@ -119,14 +118,14 @@ export class CurrentUserUtils {
             return DocUtils.AssignOpts(clickDoc, allOpts) ?? Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, allOpts),allOpts);
         });
 
-        const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, system: true};
+        const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, isSystem: true};
         return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
     }
 
     /// Initializes templates for editing click funcs of a document 
     static setupClickEditorTemplates(doc: Doc, field = "template-clickFuncs") {
         const tempClicks = DocCast(doc[field]);
-        const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, system: true};
+        const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
             { opts: { title: "onClick"},             script: "console.log('click')"},
             { opts: { title: "onDoubleClick"},       script: "console.log('click')"},
@@ -140,7 +139,7 @@ export class CurrentUserUtils {
             return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title);
         });
 
-        const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, system: true};
+        const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true};
         return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
     }
 
@@ -152,12 +151,12 @@ export class CurrentUserUtils {
             { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
             { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
         const reqdNoteList = reqdTempOpts.map(opts => {
-            const reqdOpts = {...opts, title: "text", width:200, autoHeight: true, fitWidth: true};
+            const reqdOpts = {...opts, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
             const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
             return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
         });
 
-        const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true };
+        const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
         return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
     }
 
@@ -169,14 +168,14 @@ export class CurrentUserUtils {
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
         CurrentUserUtils.setupChildClickEditors(doc)
-        const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
+        const reqdOpts = { title: "template layouts", _xMargin: 0, isSystem: true,  };
         const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
         return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
     static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
-        const reqdOpts = { title: "icon templates", _height: 75, system: true };
+        const reqdOpts = { title: "icon templates", _height: 75, isSystem: true };
         const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
@@ -187,7 +186,7 @@ export class CurrentUserUtils {
                 case DocumentType.IMG : creator = imageBox; break;
                 case DocumentType.FONTICON: creator = fontBox; break;
             }
-            const allopts = {system: true, onClickScriptDisable: "never",  ...opts};
+            const allopts = {isSystem: true, onClickScriptDisable: "never",  ...opts};
             return DocUtils.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
                       DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
                 {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
@@ -195,14 +194,14 @@ export class CurrentUserUtils {
         const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
             textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
-        const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _showTitle: "title", ...opts });
+        const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _layout_showTitle: "title", ...opts });
         const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
         const iconTemplates = [
             makeIconTemplate(undefined,          "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}),
             makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}),
             makeIconTemplate(DocumentType.PDF,   "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}),
             makeIconTemplate(DocumentType.WEB,   "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "brown"}),
-            makeIconTemplate(DocumentType.RTF,   "text",  { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}),
+            makeIconTemplate(DocumentType.RTF,   "text",  { iconTemplate:DocumentType.LABEL, _layout_showTitle: "creationDate"}),
             makeIconTemplate(DocumentType.IMG,   "data",  { iconTemplate:DocumentType.IMG,   _height: undefined}),
             makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
             makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
@@ -220,7 +219,7 @@ export class CurrentUserUtils {
         title: string, toolTip: string, icon: string,  ignoreClick?: boolean, dragFactory?: Doc, 
         backgroundColor?: string, openFactoryAsDelegate?:boolean, openFactoryLocation?:string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string},
     }[] {
-        const standardOps = (key:string) => ({ title : "Untitled "+ key, _fitWidth: false, system: true, "dragFactory-count": 0, cloneFieldFilter: new List<string>(["system"]) });
+        const standardOps = (key:string) => ({ title : "Untitled "+ key, _layout_fitWidth: false, isSystem: true, "dragFactory_count": 0, cloneFieldFilter: new List<string>(["isSystem"]) });
         const json = {
             doc: {
                 type: "doc",
@@ -247,7 +246,7 @@ export class CurrentUserUtils {
                 "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
                 `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
                 "    <FormattedTextBox {...props} dontScale='true' fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize||9}px' height='{(this._headerHeight||0)}px' backgroundColor='{this._headerColor || MySharedDocs().userColor||`lightGray`}' />" +
-                `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' backgroundColor='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) + (this._autoHeightMargins=this._headerHeight ? this._headerHeight+${headerBtnHgt}:0)’}  >Metadata</HTMLdiv>` +
+                `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' backgroundColor='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) + (this._layout_autoHeightMargins=this._headerHeight ? this._headerHeight+${headerBtnHgt}:0)’}  >Metadata</HTMLdiv>` +
                 "</HTMLdiv>"
             }, "header");
 
@@ -263,27 +262,27 @@ export class CurrentUserUtils {
                           funcs?:{[key:string]: any},  // computed fields that are rquired for the empth thing
                           creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
               }[] = [
-            {key: "Note",       creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _autoHeight: true }},
-            {key: "Flashcard",  creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _autoHeight: true, _showAltContentUI: true}},
-            {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, _backgroundGridShow: true, }},
-            {key: "Noteboard",  creator: opts => Docs.Create.NoteTakingDocument([], opts),  opts: { _width: 250, _height: 200, _fitWidth: true}},
-            {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 150, _height: 100, _fitWidth: true }},
-            {key: "Webpage",    creator: opts => Docs.Create.WebDocument("",opts),          opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }},
+            {key: "Note",       creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _layout_autoHeight: true }},
+            {key: "Flashcard",  creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _layout_autoHeight: true, _layout_altContentUI: true}},
+            {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, }},
+            {key: "Noteboard",  creator: opts => Docs.Create.NoteTakingDocument([], opts),  opts: { _width: 250, _height: 200, _layout_fitWidth: true}},
+            {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 150, _height: 100, _layout_fitWidth: true }},
+            {key: "Webpage",    creator: opts => Docs.Create.WebDocument("",opts),          opts: { _width: 400, _height: 512, _nativeWidth: 850, data_useCors: true, }},
             {key: "Comparison", creator: Docs.Create.ComparisonDocument,                    opts: { _width: 300, _height: 300 }},
             {key: "Audio",      creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }},
-            {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _fitWidth: true, _showSidebar: true,  }},
+            {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _layout_fitWidth: true, _layout_showSidebar: true,  }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
-            {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
+            {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, onClick: FollowLinkScript()}},
             {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, _autoHeight: true, treeViewHideUnrendered: true}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, treeViewHideTitle: true, _fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }},
-            {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _fitWidth: true, _backgroundGridShow: true, }},
+            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeViewHideUnrendered: true}},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "embed" as any, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }},
+            {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, 
-                                                                                                treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, 
+                                                                                                treeViewHasOverlay: true, _fontSize: "20px", _layout_autoHeight: true, 
                                                                                                 allowOverlayDrop: true, treeViewType: TreeViewType.outline, 
-                                                                                                backgroundColor: "white", _xMargin: 0, _yMargin: 0, _singleLine: true 
+                                                                                                backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
 
@@ -320,16 +319,16 @@ export class CurrentUserUtils {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
             const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, 
                 _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, 
-                btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
+                btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
         });
 
         const reqdOpts:DocumentOptions = {
-            title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true,
-            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-             childDropAction: 'alias' 
+            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, isSystem: true,
+            _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
+             childDropAction: 'embed' 
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -354,7 +353,7 @@ export class CurrentUserUtils {
 
     /// the empty panel that is filled with whichever left menu button's panel has been selected
     static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
-        DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
+        DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.isSystem = true; return doc;})(new Doc()), {isSystem:true});
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
@@ -364,8 +363,8 @@ export class CurrentUserUtils {
         const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
-                title, icon, target, btnType: ButtonType.MenuButton, system: true, dontUndo: true,  dontRegisterView: true,
-                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+                title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true,  dontRegisterView: true,
+                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
             };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
@@ -373,14 +372,14 @@ export class CurrentUserUtils {
 
         const reqdStackOpts:DocumentOptions ={
             title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
-            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true,
+            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
     }
 
     // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
     static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") {
-        const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,};
+        const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, isSystem: true, _chromeHidden: true,};
         DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
     }
 
@@ -401,9 +400,9 @@ export class CurrentUserUtils {
                 title: data.title,
                 _lockedPosition: true,
                 onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
-                backgroundColor: data.backgroundColor, system: true
+                backgroundColor: data.backgroundColor, isSystem: true
             },
-                [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
+                [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", isSystem: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
         );
     }
 
@@ -411,51 +410,51 @@ export class CurrentUserUtils {
     static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, {
         ...opts,
          _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15,
-        borderRounding: "5px", boxShadow: "0 0", system: true
+        borderRounding: "5px", boxShadow: "0 0", isSystem: true
     }) as any as Doc
 
     // sets up the text container for the information contained within the mobile button
     static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, {
         ...opts,
         _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25,
-        backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, system: true
+        backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, isSystem: true
     }) as any as Doc
 
     // Sets up the title of the button
     static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, {
         ...opts,
-        title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", system: true
+        title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", isSystem: true
     }) as any as Doc
 
     // Sets up the description of the button
     static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, {
         ...opts,
-        title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true
+        title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, isSystem: true
     }) as any as Doc
 
 
 
     static setupMobileInkingDoc(userDoc: Doc) {
-        return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true });
+        return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", isSystem: true });
     }
 
     static setupMobileUploadDoc(userDoc: Doc) {
         // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" })
         const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", {
-            title: "Upload Images From the Web", _lockedPosition: true, system: true
+            title: "Upload Images From the Web", _lockedPosition: true, isSystem: true
         });
         const uploadDoc = Docs.Create.StackingDocument([], {
-            title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, system: true, _chromeHidden: true,
+            title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, isSystem: true, _chromeHidden: true,
         });
         return Docs.Create.StackingDocument([webDoc, uploadDoc], {
-            _width: screen.width, _lockedPosition: true, title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", system: true, _chromeHidden: true,
+            _width: screen.width, _lockedPosition: true, title: "Upload", _layout_autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", isSystem: true, _chromeHidden: true,
         });
     }
 
     /// Search option on the left side button panel
     static setupSearcher(doc: Doc, field:string) {
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", 
+                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDropAction: "embed", 
                 _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true,  });
     }
 
@@ -465,8 +464,8 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
         const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
-            title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
-            _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+            title: "My Tools", isSystem: true, ignoreClick: true, boxShadow: "0 0",
+            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
     }
@@ -479,7 +478,7 @@ export class CurrentUserUtils {
         const newDashboard = `createNewDashboard()`;
 
         const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
-             title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
+             title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true };
         const reqdBtnScript = {onClick: newDashboard,}
         const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
        
@@ -492,9 +491,9 @@ export class CurrentUserUtils {
         const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true,
-            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
-            buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "alias",
-            _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
+            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
+            buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "embed",
+            _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
             childContextMenuLabels:new List<string>(childContextMenuLabels),
@@ -517,20 +516,20 @@ export class CurrentUserUtils {
     /// initializes the left sidebar File system pane
     static setupFilesystem(doc: Doc, field:string) {
         var myFilesystem = DocCast(doc[field]);
-        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, isSystem: true, isFolder: true });
         
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
             _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30,
-            title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true
+            title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: newFolder};
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
-        const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
-            title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true,
+        const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
+            title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true,
             isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, 
-            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "alias",
+            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed",
             childContextMenuLabels: new List<string>(["Create new folder"]),
             childContextMenuIcons: new List<string>(["plus"]),
             explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
@@ -545,8 +544,8 @@ export class CurrentUserUtils {
 
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
-        const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
-            title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true,
+        const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
+            title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true,
             treeViewTruncateTitleWidth: 350, ignoreClick: true,  boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
@@ -556,7 +555,7 @@ export class CurrentUserUtils {
 
         const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
         const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, 
-            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", system: true,
+            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", isSystem: true,
             toolTip: "Empty recently closed",};
         const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
 
@@ -572,7 +571,7 @@ export class CurrentUserUtils {
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
-            boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
+            boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true,
             treeViewHideTitle: true, treeViewTruncateTitleWidth: 350
         };
         if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
@@ -582,13 +581,13 @@ export class CurrentUserUtils {
     static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
         ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-        _lockedPosition: true, system: true, flexDirection: "row"
+        _lockedPosition: true, isSystem: true, flexDirection: "row"
     })
 
     static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
         btnType: ButtonType.ToolButton, _forceActive: true, _hideContextMenu: true, 
         _removeDropProperties: new List<string>([ "_hideContextMenu", "stayInCollection"]), 
-        _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts,
+        _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts,
     })
 
     /// initializes the required buttons in the expanding button menu at the bottom of the Dash window
@@ -606,7 +605,7 @@ export class CurrentUserUtils {
         ];
         const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
-            title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'alias',
+            title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'embed',
             childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
         };
         reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
@@ -623,7 +622,7 @@ export class CurrentUserUtils {
     }
     static viewTools(): Button[] {
         return [
-            { title: "Snap\xA0Lines", icon: "th",           toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Snap\xA0Lines", icon: "th",           toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snaplines",  funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Grid",          icon: "border-all",   toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "View\xA0All",   icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Clusters",      icon: "braille",      toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
@@ -635,7 +634,7 @@ export class CurrentUserUtils {
         return [
             { title: "Font",    toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
-            { title: "Size",    toolTip: "Font size (%size)",    btnType: ButtonType.NumberButton, width: 75,           toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions },
+            { title: "Size",    toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton, width: 75,           toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 },
             { title: "Color",   toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}},
             { title: "Highlight",toolTip:"Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
             { title: "Bold",    toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
@@ -663,7 +662,7 @@ export class CurrentUserUtils {
             { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square",     toolType:GestureUtils.Gestures.Rectangle,   scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} },
             { title: "Line",   toolTip: "Line (double tap to lock mode)",   btnType: ButtonType.ToggleButton, icon: "minus",      toolType:GestureUtils.Gestures.Line,        scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} },
             { title: "Mask",   toolTip: "Mask",                             btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask",                        scripts: {onClick:'{ return setInkProperty(self.toolType, value, _readOnly_);}'} },
-            { title: "Width",  toolTip: "Stroke width",                     btnType: ButtonType.NumberButton,                     toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1},
+            { title: "Width",  toolTip: "Stroke width",                     btnType: ButtonType.NumberSliderButton,               toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnMin: 1},
             { title: "Ink",    toolTip: "Stroke color",                     btnType: ButtonType.ColorButton,  icon: "pen",        toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'} },
         ];
     }
@@ -712,7 +711,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
             backgroundColor: params.backgroundColor ??"transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
-            color: Colors.WHITE, system: true, dontUndo: true, 
+            color: Colors.WHITE, isSystem: true, dontUndo: true, 
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
             toolType: params.toolType, expertMode: params.expertMode,
@@ -728,7 +727,7 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'alias', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
@@ -750,11 +749,11 @@ export class CurrentUserUtils {
 
     /// collection of documents rendered in the overlay layer above all tabs and other UI
     static setupOverlays(doc: Doc, field = "myOverlayDocs") {
-        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", isSystem: true });
     }
 
     static setupPublished(doc:Doc, field = "myPublishedDocs") {
-        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", isSystem: true });
     }
     
     /// The database of all links on all documents
@@ -788,9 +787,9 @@ export class CurrentUserUtils {
             childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
-            childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
-            // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar
-            _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
+            childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
+            // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
+            _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
             explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
         };
         
@@ -800,16 +799,16 @@ export class CurrentUserUtils {
     /// Import option on the left side button panel 
     static setupImportSidebar(doc: Doc, field:string) {   
         const reqdOpts:DocumentOptions = {
-            title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _showTitle: "title", 
+            title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _layout_showTitle: "title", 
             _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
-            childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
+            childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
             dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
         };
         const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
     
         const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
             _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
-            buttonText: "Import", icon: "upload", system: true };
+            buttonText: "Import", icon: "upload", isSystem: true };
         DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
         return myImports;
     }
@@ -818,13 +817,13 @@ export class CurrentUserUtils {
     /// whether to revert to "default" values, or to leave them as the user/system last set them.
     static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
-        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
+        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data_modificationDate"]),
             async () => {
                 const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
                 SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
-        doc.system ?? (doc.system = true);
+        doc.isSystem ?? (doc.isSystem = true);
         doc.title ?? (doc.title = Doc.CurrentUserEmail);
         Doc.noviceMode ?? (Doc.noviceMode = true);
         doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
@@ -860,7 +859,7 @@ export class CurrentUserUtils {
         this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true }); // drop down panel at top of dashboard for stashing documents
         
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
 
@@ -874,13 +873,13 @@ export class CurrentUserUtils {
         return doc;
     }
     static setupFieldInfos(doc:Doc, field="fieldInfos") {
-        const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
+        const fieldInfoOpts = { title: "Field Infos", isSystem: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
         const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
         const entries = Object.entries(new DocumentOptions());
         entries.forEach(pair => {
             if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) {
                 const options = pair[1] as FInfo;
-                const opts:DocumentOptions = { system: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")};
+                const opts:DocumentOptions = { isSystem: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")};
                 switch (options.fieldType) {
                     case "boolean": opts.fieldValues = new List<boolean>(options.values as any); break;
                     case "number": opts.fieldValues = new List<number>(options.values as any); break;
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index e116ae14b..1cce21034 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -327,7 +327,7 @@ export namespace DictationManager {
                 'new outline',
                 {
                     action: (target: DocumentView) => {
-                        const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _autoHeight: true });
+                        const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _layout_autoHeight: true });
                         const proto = newBox.proto!;
                         const prompt = 'Press alt + r to start dictating here...';
                         const head = 3;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 695b003a6..6eed4dc86 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -69,12 +69,12 @@ export class DocumentManager {
     public AddView = (view: DocumentView) => {
         //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
         if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const viewAnchorIndex = view.props.LayoutTemplateString.includes('anchor2') ? 'anchor2' : 'anchor1';
+            const viewAnchorIndex = view.props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
             const link = view.rootDoc;
             this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
                 this.LinkedDocumentViews.push({
-                    a: viewAnchorIndex === 'anchor2' ? otherView : view,
-                    b: viewAnchorIndex === 'anchor2' ? view : otherView,
+                    a: viewAnchorIndex === 'link_anchor_2' ? otherView : view,
+                    b: viewAnchorIndex === 'link_anchor_2' ? view : otherView,
                     l: link,
                 })
             );
@@ -173,7 +173,7 @@ export class DocumentManager {
 
         // heuristic to return the "best" documents first:
         //   choose a document in the lightbox first
-        //   choose an exact match over an alias match
+        //   choose an exact match over an embedding match
         lightViews.map(view => view.rootDoc === toFind && toReturn.push(view));
         lightViews.map(view => view.rootDoc !== toFind && Doc.AreProtosEqual(view.rootDoc, toFind) && toReturn.push(view));
         docViews.map(view => view.rootDoc === toFind && toReturn.push(view));
@@ -184,15 +184,15 @@ export class DocumentManager {
 
     static GetContextPath(doc: Opt<Doc>, includeExistingViews?: boolean) {
         if (!doc) return [];
-        const srcContext = DocCast(doc.annotationOn, DocCast(doc.context));
+        const srcContext = DocCast(doc.annotationOn, DocCast(doc.embedContainer));
         var containerDocContext = srcContext ? [srcContext, doc] : [doc];
         while (
             containerDocContext.length &&
-            containerDocContext[0]?.context &&
-            DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking &&
+            containerDocContext[0]?.embedContainer &&
+            DocCast(containerDocContext[0].embedContainer)?.viewType !== CollectionViewType.Docking &&
             (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0]))
         ) {
-            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+            containerDocContext = [Cast(containerDocContext[0].embedContainer, Doc, null), ...containerDocContext];
         }
         return containerDocContext;
     }
@@ -256,7 +256,7 @@ export class DocumentManager {
         if (options.openLocation === OpenWhere.lightbox) {
             // even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox)
             const target = DocCast(targetDoc.annotationOn, targetDoc);
-            const contextView = this.getDocumentView(DocCast(target.context));
+            const contextView = this.getDocumentView(DocCast(target.embedContainer));
             if (contextView?.docView?._componentView?.addDocTab?.(target, OpenWhere.lightbox)) {
                 await new Promise<void>(waitres => setTimeout(() => waitres()));
             }
@@ -275,7 +275,7 @@ export class DocumentManager {
     focusViewsInPath = async (docView: DocumentView, options: DocFocusOptions, iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt<Doc>; childDocView: Opt<DocumentView> }>) => {
         let contextView: DocumentView | undefined; // view containing context that contains target
         while (true) {
-            docView.rootDoc.layoutKey === 'layout_icon' ? await new Promise<void>(res => docView.iconify(res)) : undefined;
+            docView.rootDoc.layout_fieldKey === 'layout_icon' ? await new Promise<void>(res => docView.iconify(res)) : undefined;
             docView.props.focus(docView.rootDoc, options); // focus the view within its container
             const { childDocView, viewSpec } = await iterator(docView);
             if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView };
@@ -315,7 +315,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
         if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) {
             DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc));
         } else {
-            const container = DocCast(containingDoc ?? doc.context);
+            const container = DocCast(containingDoc ?? doc.embedContainer);
             const showDoc = !Doc.IsSystem(container) ? container : doc;
             options.toggleTarget = undefined;
             DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index b6de5604d..070e0f918 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,7 +15,7 @@ import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
 
-export type dropActionType = 'alias' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties
+export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties
 
 /**
  * Initialize drag
@@ -131,7 +131,7 @@ export namespace DragManager {
         canEmbed?: boolean;
         userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
         defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
-        dropAction: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'alias', but the document is dropped within the same collection, the drop action will be switched to 'move'
+        dropAction: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
         removeDropProperties?: string[];
         moveDocument?: MoveFunction;
         removeDocument?: RemoveFunction;
@@ -193,7 +193,7 @@ export namespace DragManager {
         };
     }
 
-    // drag a document and drop it (or make an alias/copy on drop)
+    // drag a document and drop it (or make an embed/copy on drop)
     export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, dropEvent?: () => any) {
         const addAudioTag = (dropDoc: any) => {
             dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField());
@@ -210,8 +210,8 @@ export namespace DragManager {
                         dragData.draggedDocuments.map(async d =>
                             !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
                                 ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
-                                : docDragData.dropAction === 'alias'
-                                ? Doc.BestAlias(d)
+                                : docDragData.dropAction === 'embed'
+                                ? Doc.BestEmbedding(d)
                                 : docDragData.dropAction === 'proto'
                                 ? Doc.GetProto(d)
                                 : docDragData.dropAction === 'copy'
@@ -487,7 +487,7 @@ export namespace DragManager {
         const moveHandler = (e: PointerEvent) => {
             e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
             if (dragData instanceof DocumentDragData) {
-                dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'alias' : dragData.defaultDropAction;
+                dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'embed' : dragData.defaultDropAction;
             }
             if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header' || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
                 if (!startWindowDragTimer) {
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 2e1d6ba23..f46ea393a 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -58,8 +58,8 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
         // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
         if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) {
             if (data.removeDropProperties || dbox.removeDropProperties) {
-                //dbox = Doc.MakeAlias(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
-                dbox = Doc.MakeAlias(dbox);
+                //dbox = Doc.MakeEmbedding(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
+                dbox = Doc.MakeEmbedding(dbox);
                 const dragProps = Cast(dbox.removeDropProperties, listSpec('string'), []);
                 const remProps = (data.removeDropProperties || []).concat(Array.from(dragProps));
                 remProps.map(prop => (dbox[prop] = undefined));
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index c8b784390..da947aba6 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -155,7 +155,7 @@ export class GroupManager extends React.Component<{}> {
     addGroup(groupDoc: Doc): boolean {
         if (this.GroupManagerDoc) {
             Doc.AddDocToList(this.GroupManagerDoc, 'data', groupDoc);
-            this.GroupManagerDoc['data-lastModified'] = new DateField();
+            this.GroupManagerDoc['data_modificationDate'] = new DateField();
             return true;
         }
         return false;
@@ -176,7 +176,7 @@ export class GroupManager extends React.Component<{}> {
                     const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group);
                     index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1);
                 }
-                this.GroupManagerDoc['data-lastModified'] = new DateField();
+                this.GroupManagerDoc['data_modificationDate'] = new DateField();
                 if (group === this.currentGroup) {
                     this.currentGroup = undefined;
                 }
@@ -197,7 +197,7 @@ export class GroupManager extends React.Component<{}> {
             !memberList.includes(email) && memberList.push(email);
             groupDoc.members = JSON.stringify(memberList);
             SharingManager.Instance.shareWithAddedMember(groupDoc, email);
-            this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
+            this.GroupManagerDoc && (this.GroupManagerDoc['data_modificationDate'] = new DateField());
         }
     }
 
@@ -214,7 +214,7 @@ export class GroupManager extends React.Component<{}> {
                 const user = memberList.splice(index, 1)[0];
                 groupDoc.members = JSON.stringify(memberList);
                 SharingManager.Instance.removeMember(groupDoc, email);
-                this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
+                this.GroupManagerDoc && (this.GroupManagerDoc['data_modificationDate'] = new DateField());
             }
         }
     }
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 297520d5d..151f18d6f 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -20,7 +20,7 @@ export namespace Hypothesis {
     export const getSourceWebDoc = async (uri: string) => {
         const result = await findWebDoc(uri);
         console.log(result ? 'existing doc found' : 'existing doc NOT found');
-        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
+        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, data_useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
     };
 
     /**
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 9bd92a316..55d37f544 100644
--- a/src/client/util/Import & Export/ImageUtils.ts	
+++ b/src/client/util/Import & Export/ImageUtils.ts	
@@ -1,12 +1,11 @@
-import { Doc } from "../../../fields/Doc";
-import { ImageField } from "../../../fields/URLField";
-import { Cast, StrCast, NumCast } from "../../../fields/Types";
-import { Networking } from "../../Network";
-import { Id } from "../../../fields/FieldSymbols";
-import { Utils } from "../../../Utils";
+import { Doc } from '../../../fields/Doc';
+import { ImageField } from '../../../fields/URLField';
+import { Cast, StrCast, NumCast } from '../../../fields/Types';
+import { Networking } from '../../Network';
+import { Id } from '../../../fields/FieldSymbols';
+import { Utils } from '../../../Utils';
 
 export namespace ImageUtils {
-
     export const ExtractExif = async (document: Doc): Promise<boolean> => {
         const field = Cast(document.data, ImageField);
         if (!field) {
@@ -17,23 +16,22 @@ export namespace ImageUtils {
             contentSize,
             nativeWidth,
             nativeHeight,
-            exifData: { error, data }
-        } = await Networking.PostToServer("/inspectImage", { source });
+            exifData: { error, data },
+        } = await Networking.PostToServer('/inspectImage', { source });
         document.exif = error || Doc.Get.FromJson({ data });
         const proto = Doc.GetProto(document);
-        nativeWidth && (document._height = NumCast(document._width) * nativeHeight / nativeWidth);
-        proto["data-nativeWidth"] = nativeWidth;
-        proto["data-nativeHeight"] = nativeHeight;
-        proto["data-path"] = source;
-        proto.contentSize = contentSize ? contentSize : undefined;
+        nativeWidth && (document._height = (NumCast(document._width) * nativeHeight) / nativeWidth);
+        proto['data_nativeWidth'] = nativeWidth;
+        proto['data_nativeHeight'] = nativeHeight;
+        proto['data-path'] = source;
+        proto.data_contentSize = contentSize ? contentSize : undefined;
         return data !== undefined;
     };
 
     export const ExportHierarchyToFileSystem = async (collection: Doc): Promise<void> => {
-        const a = document.createElement("a");
+        const a = document.createElement('a');
         a.href = Utils.prepend(`/imageHierarchyExport/${collection[Id]}`);
         a.download = `Dash Export [${StrCast(collection.title)}].zip`;
         a.click();
     };
-
-}
\ No newline at end of file
+}
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 9591dbed3..043f0f1f3 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -161,6 +161,7 @@ export namespace InteractionUtils {
                                 <polygon
                                     style={{ stroke: color }}
                                     strokeLinejoin={lineJoin as any}
+                                    strokeDasharray={dashArray}
                                     strokeWidth={(markerStrokeWidth * 2) / 3}
                                     points={`0 ${-markerStrokeWidth * arrowWidthFactor}, ${markerStrokeWidth * arrowNotchFactor} 0, 0 ${markerStrokeWidth * arrowWidthFactor}, ${arrowLengthFactor * markerStrokeWidth} 0`}
                                 />
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index ba2edb65c..246f5bb8c 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -12,8 +12,8 @@ import { SelectionManager } from './SelectionManager';
 import { UndoManager } from './UndoManager';
 /*
  * link doc:
- * - anchor1: doc
- * - anchor2: doc
+ * - link_anchor_1: doc
+ * - link_anchor_2: doc
  *
  * group doc:
  * - type: string representing the group type/name/category
@@ -43,10 +43,10 @@ export class LinkFollower {
 
     public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) {
         const linkDocs = link ? [link] : LinkManager.Links(sourceDoc);
-        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
-        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
-        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor2 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor2 as Doc).annotationOn) : (d.anchor2 as Doc)).length === 0);
-        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor1 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor1 as Doc).annotationOn) : (d.anchor1 as Doc)).length === 0);
+        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.link_anchor_1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is link_anchor_1
+        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.link_anchor_2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.link_anchor_2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is link_anchor_2
+        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.link_anchor_2 as Doc).type === DocumentType.MARKER ? DocCast((d.link_anchor_2 as Doc).annotationOn) : (d.link_anchor_2 as Doc)).length === 0);
+        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews((d.link_anchor_1 as Doc).type === DocumentType.MARKER ? DocCast((d.link_anchor_1 as Doc).annotationOn) : (d.link_anchor_1 as Doc)).length === 0);
         const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
         const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs;
         const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1);
@@ -55,13 +55,13 @@ export class LinkFollower {
         if (!followLinks.length) finished?.();
         followLinks.forEach(async linkDoc => {
             const target = (
-                sourceDoc === linkDoc.anchor1
-                    ? linkDoc.anchor2
-                    : sourceDoc === linkDoc.anchor2
-                    ? linkDoc.anchor1
-                    : Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)
-                    ? linkDoc.anchor2
-                    : linkDoc.anchor1
+                sourceDoc === linkDoc.link_anchor_1
+                    ? linkDoc.link_anchor_2
+                    : sourceDoc === linkDoc.link_anchor_2
+                    ? linkDoc.link_anchor_1
+                    : Doc.AreProtosEqual(sourceDoc, linkDoc.link_anchor_1 as Doc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc)
+                    ? linkDoc.link_anchor_2
+                    : linkDoc.link_anchor_1
             ) as Doc;
             if (target) {
                 const doFollow = (canToggle?: boolean) => {
@@ -92,16 +92,16 @@ export class LinkFollower {
                 };
                 let movedTarget = false;
                 if (sourceDoc.followLinkLocation === OpenWhere.inParent) {
-                    const sourceDocParent = DocCast(sourceDoc.context);
-                    if (target.context instanceof Doc && target.context !== sourceDocParent) {
-                        Doc.RemoveDocFromList(target.context, Doc.LayoutFieldKey(target.context), target);
+                    const sourceDocParent = DocCast(sourceDoc.embedContainer);
+                    if (target.embedContainer instanceof Doc && target.embedContainer !== sourceDocParent) {
+                        Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutFieldKey(target.embedContainer), target);
                         movedTarget = true;
                     }
                     if (!DocListCast(sourceDocParent[Doc.LayoutFieldKey(sourceDocParent)]).includes(target)) {
                         Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target);
                         movedTarget = true;
                     }
-                    target.context = sourceDocParent;
+                    target.embedContainer = sourceDocParent;
                     const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
                     if (sourceDoc.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
                         target.x = moveTo[0];
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 555215417..5e6107e5f 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -7,8 +7,8 @@ import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
 import { ScriptingGlobals } from './ScriptingGlobals';
 /*
  * link doc:
- * - anchor1: doc
- * - anchor2: doc
+ * - link_anchor_1: doc
+ * - link_anchor_2: doc
  *
  * group doc:
  * - type: string representing the group type/name/category
@@ -33,7 +33,7 @@ export class LinkManager {
         await Promise.all(
             ((await DocListCastAsync(linkDb.data)) ?? []).map(link =>
                 // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
-                [PromiseValue(link?.anchor1), PromiseValue(link?.anchor2)]
+                [PromiseValue(link?.link_anchor_1), PromiseValue(link?.link_anchor_2)]
             )
         );
         LinkManager.userLinkDBs.push(linkDb);
@@ -42,13 +42,13 @@ export class LinkManager {
     static _links: Doc[] = [];
     constructor() {
         LinkManager._instance = this;
-        this.createLinkrelationshipLists();
+        this.createlink_relationshipLists();
         LinkManager.userLinkDBs = [];
         const addLinkToDoc = (link: Doc) => {
             Promise.all([link]).then(linkdoc => {
                 const link = DocCast(linkdoc[0]);
                 Promise.all([link.proto]).then(linkproto => {
-                    Promise.all([link.anchor1, link.anchor2]).then(linkdata => {
+                    Promise.all([link.link_anchor_1, link.link_anchor_2]).then(linkdata => {
                         const a1 = DocCast(linkdata[0]);
                         const a2 = DocCast(linkdata[1]);
                         a1 &&
@@ -64,8 +64,8 @@ export class LinkManager {
             });
         };
         const remLinkFromDoc = (link: Doc) => {
-            const a1 = link?.anchor1;
-            const a2 = link?.anchor2;
+            const a1 = link?.link_anchor_1;
+            const a2 = link?.link_anchor_2;
             Promise.all([a1, a2]).then(
                 action(() => {
                     if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
@@ -130,11 +130,11 @@ export class LinkManager {
         LinkManager.addLinkDB(Doc.LinkDBDoc());
     }
 
-    public createLinkrelationshipLists = () => {
+    public createlink_relationshipLists = () => {
         //create new lists for link relations and their associated colors if the lists don't already exist
-        !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List<string>());
+        !Doc.UserDoc().link_relationshipList && (Doc.UserDoc().link_relationshipList = new List<string>());
         !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>());
-        !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List<number>());
+        !Doc.UserDoc().link_relationshipSizes && (Doc.UserDoc().link_relationshipSizes = new List<number>());
     };
 
     public addLink(linkDoc: Doc, checkExists = false) {
@@ -162,7 +162,7 @@ export class LinkManager {
             return [];
         }
         const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
-        const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '-annotations']);
+        const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '_annotations']);
         if (!annos) debugger;
         return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
     }, true);
@@ -171,9 +171,9 @@ export class LinkManager {
     public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
         const anchorGroups = new Map<string, Array<Doc>>();
         this.relatedLinker(anchor).forEach(link => {
-            if (link.linkRelationship && link.linkRelationship !== '-ungrouped-') {
-                const relation = StrCast(link.linkRelationship);
-                const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+            if (link.link_relationship && link.link_relationship !== '-ungrouped-') {
+                const relation = StrCast(link.link_relationship);
+                const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), anchor) ? 0 : 1] : relation;
                 const group = anchorGroups.get(anchorRelation);
                 anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
             } else {
@@ -189,8 +189,8 @@ export class LinkManager {
     //TODO This should probably return undefined if there isn't an opposite anchor
     //TODO This should also await the return value of the anchor so we don't filter out promises
     public static getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
-        const a1 = Cast(linkDoc.anchor1, Doc, null);
-        const a2 = Cast(linkDoc.anchor2, Doc, null);
+        const a1 = Cast(linkDoc.link_anchor_1, Doc, null);
+        const a2 = Cast(linkDoc.link_anchor_2, Doc, null);
         if (Doc.AreProtosEqual(anchor, a1)) return a2;
         if (Doc.AreProtosEqual(anchor, a2)) return a1;
         if (Doc.AreProtosEqual(anchor, a1.annotationOn as Doc)) return a2;
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index 22cca4a2e..cbc465d6a 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -127,8 +127,8 @@ export class ReplayMovements {
     zoomAndPan = (movement: Movement, document: CollectionFreeFormView) => {
         const { panX, panY, scale } = movement;
         scale !== 0 && document.zoomSmoothlyAboutPt([panX, panY], scale, 0);
-        document.Document._panX = panX;
-        document.Document._panY = panY;
+        document.Document._freeform_panX = panX;
+        document.Document._freeform_panY = panY;
     };
 
     getFirstMovements = (movements: Movement[]): Map<Doc, Movement> => {
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 79759a71d..d154c48a4 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -24,51 +24,57 @@ export namespace SearchUtil {
 
     export interface SearchParams {
         hl?: string;
-        "hl.fl"?: string;
+        'hl.fl'?: string;
         start?: number;
         rows?: number;
         fq?: string;
         sort?: string;
-        allowAliases?: boolean;
-        onlyAliases?: boolean;
-        "facet"?: string;
-        "facet.field"?: 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");
+        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.onlyAliases) {
-            const header = query.match(/_[atnb]?:/) ? replacedQuery : "DEFAULT:" + replacedQuery;
+        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);
+        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 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[][] = [];
-        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]));
-            }));
-        }
-
+        // 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[][] = [];
@@ -86,7 +92,7 @@ export namespace SearchUtil {
         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.allowAliases || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
+            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 {
@@ -97,48 +103,46 @@ export namespace SearchUtil {
         return { docs: theDocs, numFound: Math.max(0, result.numFound), highlighting, lines: theLines };
     }
 
-    export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
-    export async function GetAliasesOfDocument(doc: Doc, returnDocs: false): Promise<string[]>;
-    export async function GetAliasesOfDocument(doc: Doc, returnDocs = true): Promise<Doc[] | string[]> {
+    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}"`, allowAliases: true })).docs;
+            return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).docs;
         } else {
-            return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).ids;
+            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]}"` });
+        const results = await Search('', true, { fq: `proto_i:"${doc[Id]}"` });
         return results.docs;
     }
 
-    export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[], aliasContexts: Doc[] }> {
-        const docContexts = (await Search("", true, { fq: `data_l:"${doc[Id]}"` })).docs;
-        const aliases = await GetAliasesOfDocument(doc, false);
-        const aliasContexts = (await Promise.all(aliases.map(doc => Search("", true, { fq: `data_l:"${doc}"` }))));
-        const contexts = { contexts: docContexts, aliasContexts: [] as Doc[] };
-        aliasContexts.forEach(result => contexts.aliasContexts.push(...result.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[], aliasContexts: string[] }> {
-        const docContexts = (await Search("", false, { fq: `data_l:"${doc[Id]}"` })).ids;
-        const aliases = await GetAliasesOfDocument(doc, false);
-        const aliasContexts = (await Promise.all(aliases.map(doc => Search("", false, { fq: `data_l:"${doc}"` }))));
-        const contexts = { contexts: docContexts, aliasContexts: [] as string[] };
-        aliasContexts.forEach(result => contexts.aliasContexts.push(...result.ids));
+    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 query = '*';
         const response = await rp.get(Utils.prepend('/dashsearch'), {
-            qs:
-                { start: 0, rows: 10000, q: query },
-
+            qs: { start: 0, rows: 10000, q: query },
         });
         const result: IdSearchResult = JSON.parse(response);
         const { ids, numFound, highlighting } = result;
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 396d754b6..a3a924b75 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -74,7 +74,7 @@ export class SettingsManager extends React.Component<{}> {
     };
 
     @undoBatch selectUserMode = action((e: React.ChangeEvent) => (Doc.noviceMode = (e.currentTarget as any)?.value === 'Novice'));
-    @undoBatch changeShowTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().showTitle = (e.currentTarget as any).value ? 'title' : undefined));
+    @undoBatch changelayout_showTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().layout_showTitle = (e.currentTarget as any).value ? 'title' : undefined));
     @undoBatch changeFontFamily = action((e: React.ChangeEvent) => (Doc.UserDoc().fontFamily = (e.currentTarget as any).value));
     @undoBatch changeFontSize = action((e: React.ChangeEvent) => (Doc.UserDoc().fontSize = (e.currentTarget as any).value));
     @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex)));
@@ -176,7 +176,7 @@ export class SettingsManager extends React.Component<{}> {
         return (
             <div className="prefs-content">
                 <div>
-                    <input type="checkbox" onChange={e => (Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().showTitle !== undefined} />
+                    <input type="checkbox" onChange={e => (Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().layout_showTitle !== undefined} />
                     <div className="preferences-check">Show doc header</div>
                 </div>
                 <div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 4937866f8..0eecb31cf 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -172,7 +172,7 @@ export class SharingManager extends React.Component<{}> {
 
                 this.setDashboardBackground(doc, permission as SharingPermissions);
                 if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
-                else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
+                return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
             })
             .some(success => !success);
     };
@@ -215,7 +215,7 @@ export class SharingManager extends React.Component<{}> {
                     return users
                         .map(({ user, sharingDoc }) => {
                             if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
-                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc); // remove the doc from the list if it already exists
+                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
                         })
                         .some(success => !success);
                 }
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 2c0a1da8b..ed9819fc0 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -40,9 +40,9 @@ export namespace SnappingManager {
     }
 
     export function SetShowSnapLines(show: boolean) {
-        runInAction(() => (Doc.UserDoc().showSnapLines = show));
+        runInAction(() => (Doc.UserDoc().freeform_snapLines = show));
     }
     export function GetShowSnapLines() {
-        return Doc.UserDoc().showSnapLines;
+        return Doc.UserDoc().freeform_snapLines;
     }
 }
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index cb8225643..a755c5dc9 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -68,7 +68,7 @@ export class TrackMovements {
         }
 
         const disposeFunc = reaction(
-            () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0) }),
+            () => ({ x: NumCast(doc.freeform_panX, -1), y: NumCast(doc.freeform_panY, -1), scale: NumCast(doc.freeform_scale, 0) }),
             res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, doc, res.scale)
         );
         this.recordingFFViews?.set(doc, disposeFunc);
diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx
index 525c0ce5a..14c922526 100644
--- a/src/client/views/AudioWaveform.tsx
+++ b/src/client/views/AudioWaveform.tsx
@@ -1,27 +1,25 @@
-import React = require("react");
-import axios from "axios";
-import { action, computed, IReactionDisposer, reaction } from "mobx";
-import { observer } from "mobx-react";
-import Waveform from "react-audio-waveform";
-import { Doc } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { listSpec } from "../../fields/Schema";
-import { Cast } from "../../fields/Types";
-import { numberRange } from "../../Utils";
-import "./AudioWaveform.scss";
-import { Colors } from "./global/globalEnums";
-
+import React = require('react');
+import axios from 'axios';
+import { action, computed, IReactionDisposer, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import Waveform from 'react-audio-waveform';
+import { Doc } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { listSpec } from '../../fields/Schema';
+import { Cast } from '../../fields/Types';
+import { numberRange } from '../../Utils';
+import './AudioWaveform.scss';
+import { Colors } from './global/globalEnums';
 
 /**
  * AudioWaveform
- * 
+ *
  * Used in CollectionStackedTimeline to render a canvas with a visual of an audio waveform for AudioBox and VideoBox documents.
  * Uses react-audio-waveform package.
  * Bins the audio data into audioBuckets which are passed to package to render the lines.
  * Calculates new buckets each time a new zoom factor or new set of trim bounds is created and stores it in a field on the layout doc with a title indicating the bounds and zoom for that list (see audioBucketField)
  */
 
-
 export interface AudioWaveformProps {
     duration: number; // length of media clip
     rawDuration: number; // length of underlying media data
@@ -32,6 +30,7 @@ export interface AudioWaveformProps {
     zoomFactor: number;
     PanelHeight: number;
     PanelWidth: number;
+    fieldKey: string;
 }
 
 @observer
@@ -40,80 +39,71 @@ export class AudioWaveform extends React.Component<AudioWaveformProps> {
 
     _disposer: IReactionDisposer | undefined;
 
-    @computed get waveHeight() { return Math.max(50, this.props.PanelHeight); }
-
-    @computed get clipStart() { return this.props.clipStart; }
-    @computed get clipEnd() { return this.props.clipEnd; }
-    @computed get zoomFactor() { return this.props.zoomFactor; }
+    @computed get waveHeight() {
+        return Math.max(50, this.props.PanelHeight);
+    }
 
-    @computed get audioBuckets() { return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec("number"), []); }
-    audioBucketField = (start: number, end: number, zoomFactor: number) => "audioBuckets/" + "/" + start.toFixed(2).replace(".", "_") + "/" + end.toFixed(2).replace(".", "_") + "/" + (zoomFactor * 10);
+    @computed get clipStart() {
+        return this.props.clipStart;
+    }
+    @computed get clipEnd() {
+        return this.props.clipEnd;
+    }
+    @computed get zoomFactor() {
+        return this.props.zoomFactor;
+    }
 
+    @computed get audioBuckets() {
+        return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec('number'), []);
+    }
+    audioBucketField = (start: number, end: number, zoomFactor: number) => this.props.fieldKey + '_audioBuckets/' + '/' + start.toFixed(2).replace('.', '_') + '/' + end.toFixed(2).replace('.', '_') + '/' + zoomFactor * 10;
 
     componentWillUnmount() {
         this._disposer?.();
     }
 
     componentDidMount() {
-        this._disposer = reaction(() => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }),
+        this._disposer = reaction(
+            () => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }),
             ({ clipStart, clipEnd, fieldKey, zoomFactor }) => {
                 if (!this.props.layoutDoc[fieldKey]) {
                     // setting these values here serves as a "lock" to prevent multiple attempts to create the waveform at nerly the same time.
-                    const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration, 1)], listSpec("number"));
-                    this.props.layoutDoc[fieldKey] = waveform && new List<number>(waveform.slice(clipStart / this.props.rawDuration * waveform.length, clipEnd / this.props.rawDuration * waveform.length));
+                    const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration, 1)], listSpec('number'));
+                    this.props.layoutDoc[fieldKey] = waveform && new List<number>(waveform.slice((clipStart / this.props.rawDuration) * waveform.length, (clipEnd / this.props.rawDuration) * waveform.length));
                     setTimeout(() => this.createWaveformBuckets(fieldKey, clipStart, clipEnd, zoomFactor));
                 }
-            }, { fireImmediately: true });
+            },
+            { fireImmediately: true }
+        );
     }
 
     // decodes the audio file into peaks for generating the waveform
     createWaveformBuckets = async (fieldKey: string, clipStart: number, clipEnd: number, zoomFactor: number) => {
-        axios({ url: this.props.mediaPath, responseType: "arraybuffer" }).then(
-            (response) => {
-                const context = new window.AudioContext();
-                context.decodeAudioData(
-                    response.data,
-                    action((buffer) => {
-                        const rawDecodedAudioData = buffer.getChannelData(0);
-                        const startInd = clipStart / this.props.rawDuration;
-                        const endInd = clipEnd / this.props.rawDuration;
-                        const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length));
-                        const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor);
+        axios({ url: this.props.mediaPath, responseType: 'arraybuffer' }).then(response => {
+            const context = new window.AudioContext();
+            context.decodeAudioData(
+                response.data,
+                action(buffer => {
+                    const rawDecodedAudioData = buffer.getChannelData(0);
+                    const startInd = clipStart / this.props.rawDuration;
+                    const endInd = clipEnd / this.props.rawDuration;
+                    const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length));
+                    const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor);
 
-                        const bucketDataSize = Math.floor(
-                            decodedAudioData.length / numBuckets
-                        );
-                        const brange = Array.from(Array(bucketDataSize));
-                        const bucketList = numberRange(numBuckets).map(
-                            (i: number) =>
-                                brange.reduce(
-                                    (p, x, j) =>
-                                        Math.abs(
-                                            Math.max(p, decodedAudioData[i * bucketDataSize + j])
-                                        ),
-                                    0
-                                ) / 2
-                        );
-                        this.props.layoutDoc[fieldKey] = new List<number>(bucketList);
-                    })
-                );
-            }
-        );
-    }
+                    const bucketDataSize = Math.floor(decodedAudioData.length / numBuckets);
+                    const brange = Array.from(Array(bucketDataSize));
+                    const bucketList = numberRange(numBuckets).map((i: number) => brange.reduce((p, x, j) => Math.abs(Math.max(p, decodedAudioData[i * bucketDataSize + j])), 0) / 2);
+                    this.props.layoutDoc[fieldKey] = new List<number>(bucketList);
+                })
+            );
+        });
+    };
 
     render() {
         return (
             <div className="audioWaveform">
-                <Waveform
-                    color={Colors.MEDIUM_BLUE_ALT}
-                    height={this.waveHeight}
-                    barWidth={200 / this.audioBuckets.length}
-                    pos={this.props.duration}
-                    duration={this.props.duration}
-                    peaks={this.audioBuckets}
-                    progressColor={Colors.MEDIUM_BLUE_ALT}
-                />
+                <Waveform color={Colors.MEDIUM_BLUE_ALT} height={this.waveHeight} barWidth={200 / this.audioBuckets.length} pos={this.props.duration} duration={this.props.duration} peaks={this.audioBuckets} progressColor={Colors.MEDIUM_BLUE_ALT} />
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index dee161931..02c5085a2 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -358,15 +358,15 @@ export class DashboardView extends React.Component {
             y: 400,
             _width: 1500,
             _height: 1000,
-            _fitWidth: true,
-            _backgroundGridShow: true,
+            _layout_fitWidth: true,
+            _freeform_backgroundGrid: true,
             backgroundColor: background,
             title: `Untitled Tab 1`,
         };
         const title = name ? name : `Dashboard ${dashboardCount}`;
         const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
-        freeformDoc.context = dashboardDoc;
+        freeformDoc.embedContainer = dashboardDoc;
 
         // switching the tabs from the datadoc to the regular doc
         const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
@@ -396,7 +396,7 @@ export class DashboardView extends React.Component {
             btnType: ButtonType.ClickButton,
             buttonText: 'New trail',
             icon: 'plus',
-            system: true,
+            isSystem: true,
         };
         const reqdBtnScript = { onClick: `createNewPresentation()` };
         const myTrailsBtn = DocUtils.AssignScripts(Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
@@ -406,13 +406,13 @@ export class DashboardView extends React.Component {
         // but we don't want to create the list of trails here-- but rather in createDashboard
         const reqdOpts: DocumentOptions = {
             title: 'My Trails',
-            _showTitle: 'title',
+            _layout_showTitle: 'title',
             _height: 100,
             treeViewHideTitle: true,
-            _fitWidth: true,
+            _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
-            childDropAction: 'alias',
+            childDropAction: 'embed',
             treeViewTruncateTitleWidth: 150,
             ignoreClick: true,
             buttonMenu: true,
@@ -423,7 +423,7 @@ export class DashboardView extends React.Component {
             boxShadow: '0 0',
             childDontRegisterViews: true,
             targetDropAction: 'same',
-            system: true,
+            isSystem: true,
             explainer: 'All of the trails that you have created will appear here.',
         };
         const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
@@ -453,7 +453,7 @@ ScriptingGlobals.add(function resetDashboard(dashboard: Doc) {
     DashboardView.resetDashboard(dashboard);
 }, 'move all dashboard tabs to single stack');
 ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
-    DashboardView.openDashboard(Doc.MakeAlias(dashboard));
+    DashboardView.openDashboard(Doc.MakeEmbedding(dashboard));
 }, 'adds Dashboard to set of Dashboards');
 ScriptingGlobals.add(function snapshotDashboard() {
     DashboardView.snapshotDashboard();
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index eba55e30c..ffb2d92f6 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -133,7 +133,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
         @computed public get annotationKey() {
-            return this.fieldKey + (this._annotationKeySuffix() ? '-' + this._annotationKeySuffix() : '');
+            return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : '');
         }
 
         @action.bound
@@ -152,7 +152,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.context = undefined;
+                    doc.embedContainer = undefined;
                     if (recent) {
                         Doc.RemoveDocFromList(recent, 'data', doc);
                         doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
@@ -193,18 +193,19 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) {
+                const aclKeys = Object.keys(this.props.Document[AclSym] ?? {});
+                aclKeys.forEach(key =>
                     added.forEach(d => {
-                        for (const key of Object.keys(this.props.Document[AclSym])) {
-                            if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d);
+                        if (d.author === denormalizeEmail(key.substring(4)) && !d.createdFrom) {
+                            distributeAcls(key, SharingPermissions.Admin, d);
                         }
-                    });
-                }
+                    })
+                );
 
                 if (effectiveAcl === AclAugment) {
                     added.map(doc => {
                         if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
-                        doc.context = this.props.Document;
+                        doc.embedContainer = this.props.Document;
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                         Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                     });
@@ -215,7 +216,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             // only make a pushpin if we have acl's to edit the document
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
-                            doc.context = this.props.Document;
+                            doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
 
                             Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
@@ -223,7 +224,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added);
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
-                    targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now()));
+                    targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(new Date(Date.now()));
                 }
             }
             return true;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 30e41b06c..547d844ca 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -178,7 +178,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             e.preventDefault();
                             let googleDoc = await Cast(dataDoc.googleDoc, Doc);
                             if (!googleDoc) {
-                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false };
+                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, data_useCors: false };
                                 googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                                 dataDoc.googleDoc = googleDoc;
                             }
@@ -473,17 +473,17 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             </Tooltip>
         );
     }
-    @observable _aliasDown = false;
+    @observable _embedDown = false;
     onTemplateButton = action((e: React.PointerEvent): void => {
         this._tooltipOpen = false;
-        setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+        setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction);
     });
-    onAliasButtonMoved = () => {
+    onEmbedButtonMoved = () => {
         if (this._dragRef.current) {
             const dragDocView = this.view0!;
             const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
             const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            dragData.defaultDropAction = 'alias';
+            dragData.defaultDropAction = 'embed';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false });
             return true;
@@ -498,14 +498,14 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const view0 = this.view0;
         const views = this.props.views();
         return !view0 ? null : (
-            <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
+            <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
                         anchorPoint={anchorPoints.LEFT_TOP}
-                        onOpen={action(() => (this._aliasDown = true))}
-                        onClose={action(() => (this._aliasDown = false))}
+                        onOpen={action(() => (this._embedDown = true))}
+                        onClose={action(() => (this._embedDown = false))}
                         content={
-                            !this._aliasDown ? null : (
+                            !this._embedDown ? null : (
                                 <div ref={this._ref}>
                                     {' '}
                                     <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} />
@@ -564,7 +564,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc;
             const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
             if (trail !== anchor.presTrail) {
-                DocUtils.MakeLink(anchor, trail, { linkRelationship: 'link trail' });
+                DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
                 anchor.presTrail = trail;
             }
             Doc.ActivePresentation = trail;
@@ -589,7 +589,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         <LinkPopup
                             key="popup"
                             showPopup={this._showLinkPopup}
-                            linkCreated={link => (link.linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
+                            linkCreated={link => (link.layout_linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
                             linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
                             linkFrom={() => this.props.views().lastElement()?.rootDoc}
                         />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 26b64bfdd..39073d763 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -121,7 +121,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     titleFieldKey &&
                     SelectionManager.Views().forEach(d => {
                         if (titleFieldKey === 'title') {
-                            d.dataDoc['title-custom'] = !this._accumulatedTitle.startsWith('-');
+                            d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-');
                             if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
                                 Doc.RemoveDocFromList(Doc.MyPublishedDocs, undefined, d.rootDoc);
                             }
@@ -138,12 +138,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             if (curKey !== title) {
                                 if (title) {
                                     if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') {
-                                        d.rootDoc.layoutKey = `layout_${title}`;
+                                        d.rootDoc.layout_fieldKey = `layout_${title}`;
                                         d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title);
-                                        d.rootDoc[`${title}-nativeWidth`] = d.rootDoc[`${title}-nativeHeight`] = 0;
+                                        d.rootDoc[`${title}_nativeWidth`] = d.rootDoc[`${title}_nativeHeight`] = 0;
                                     }
                                 } else {
-                                    d.rootDoc.layoutKey = undefined;
+                                    d.rootDoc.layout_fieldKey = undefined;
                                 }
                             }
                         } else {
@@ -272,22 +272,22 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const selectedDocs = SelectionManager.Views();
         if (selectedDocs.length) {
             if (e.ctrlKey) {
-                // open an alias in a new tab with Ctrl Key
-                CollectionDockingView.AddSplit(Doc.BestAlias(selectedDocs[0].props.Document), OpenWhereMod.right);
+                // open an embedding in a new tab with Ctrl Key
+                CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].props.Document), OpenWhereMod.right);
             } else if (e.shiftKey) {
                 // open centered in a new workspace with Shift Key
-                const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
-                alias.context = undefined;
-                alias.x = -alias[WidthSym]() / 2;
-                alias.y = -alias[HeightSym]() / 2;
-                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: 'Tab for ' + alias.title }), OpenWhereMod.right);
+                const embedding = Doc.MakeEmbedding(selectedDocs[0].props.Document);
+                embedding.embedContainer = undefined;
+                embedding.x = -embedding[WidthSym]() / 2;
+                embedding.y = -embedding[HeightSym]() / 2;
+                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right);
             } else if (e.altKey) {
                 // open same document in new tab
                 CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, OpenWhereMod.right);
             } else {
                 var openDoc = selectedDocs[0].props.Document;
-                if (openDoc.layoutKey === 'layout_icon') {
-                    openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
+                if (openDoc.layout_fieldKey === 'layout_icon') {
+                    openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc);
                     Doc.deiconifyView(openDoc);
                 }
                 LightboxView.SetLightboxDoc(
@@ -368,8 +368,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const newloccentern = seldocview.props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]);
         const newlocenter = [newloccentern[0] - NumCast(seldocview.layoutDoc._width) / 2, newloccentern[1] - NumCast(seldocview.layoutDoc._height) / 2];
         const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI);
-        seldocview.rootDoc.rotateCenterX = final.x / NumCast(seldocview.layoutDoc._width);
-        seldocview.rootDoc.rotateCenterY = final.y / NumCast(seldocview.layoutDoc._height);
+        seldocview.rootDoc.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width);
+        seldocview.rootDoc.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height);
     };
 
     @action
@@ -386,8 +386,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             action(action(() => (this._isRotating = false))), // upEvent
             action((e, doubleTap) => {
                 if (doubleTap) {
-                    seldocview.rootDoc.rotateCenterX = 0.5;
-                    seldocview.rootDoc.rotateCenterY = 0.5;
+                    seldocview.rootDoc.rotation_centerX = 0.5;
+                    seldocview.rootDoc.rotation_centerY = 0.5;
                 }
             })
         );
@@ -586,7 +586,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     let dW = docwidth * (dWin / refWidth);
                     let dH = docheight * (dHin / refHeight);
                     const scale = docView.props.ScreenToLocalTransform().Scale;
-                    const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
+                    const modifyNativeDim = (e.ctrlKey || doc.layout_forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
                     if (nwidth && nheight) {
                         if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) {
                             docheight = (nheight / nwidth) * docwidth;
@@ -602,7 +602,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
                     let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
                     const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false;
-                    const fixedAspect = nwidth && nheight && (!doc._fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
+                    const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
                     if (fixedAspect) {
                         if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) {
                             if (dragRight && modifyNativeDim) {
@@ -610,7 +610,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     doc._nativeWidth = (actualdW / (docwidth || 1)) * Doc.NativeWidth(doc);
                                 }
                             } else {
-                                if (!doc._fitWidth || preserveNativeDim) {
+                                if (!doc._layout_fitWidth || preserveNativeDim) {
                                     actualdH = (nheight / nwidth) * actualdW;
                                     doc._height = actualdH;
                                 } else if (!modifyNativeDim || dragBotRight) {
@@ -619,14 +619,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             }
                             doc._width = actualdW;
                         } else {
-                            if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) {
+                            if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._layout_fitWidth))) {
                                 // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight
                                 // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match
                                 // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
                                 doc._nativeHeight = (actualdH / (docheight || 1)) * Doc.NativeHeight(doc);
-                                doc._autoHeight = false;
+                                doc._layout_autoHeight = false;
                             } else {
-                                if (!doc._fitWidth || preserveNativeDim) {
+                                if (!doc._layout_fitWidth || preserveNativeDim) {
                                     actualdW = (nwidth / nheight) * actualdH;
                                     doc._width = actualdW;
                                 } else if (!modifyNativeDim || dragBotRight) {
@@ -642,10 +642,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         const rotCtr = [docwidth / 2, docheight / 2];
                         const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
 
-                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
+                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '_scrollHeight']))) * docView.NativeDimScaling();
                         dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
-                        dH && (doc._autoHeight = false);
+                        dH && (doc._layout_autoHeight = false);
 
                         const rotCtr2 = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
                         const tlRotated2 = Utils.rotPt(-rotCtr2[0], -rotCtr2[1], (NumCast(doc._rotation) / 180) * Math.PI);
@@ -654,7 +654,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     }
                     doc.x = NumCast(doc.x) + dX;
                     doc.y = NumCast(doc.y) + dY;
-                    doc._lastModified = new DateField();
+                    doc._layout_modificationDate = new DateField();
                 }
                 const val = this._dragHeights.get(docView.layoutDoc);
                 if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) });
@@ -670,11 +670,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         this._resizeUndo?.end();
         SnappingManager.clearSnapLines();
 
-        // detect autoHeight gesture and apply
+        // detect layout_autoHeight gesture and apply
         SelectionManager.Views()
             .map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) }))
             .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
-            .forEach(pair => (pair.doc._autoHeight = true));
+            .forEach(pair => (pair.doc._layout_autoHeight = true));
         //need to change points for resize, or else rotation/control points will fail.
         this._inkDragDocs
             .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
@@ -713,7 +713,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     }
 
     @computed get hasIcons() {
-        return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === 'layout_icon');
+        return SelectionManager.Views().some(docView => docView.rootDoc.layout_fieldKey === 'layout_icon');
     }
 
     @observable _showRotCenter = false;
@@ -722,8 +722,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         if (SelectionManager.Views().length) {
             const seldocview = SelectionManager.Views()[0];
             const loccenter = Utils.rotPt(
-                NumCast(seldocview.rootDoc.rotateCenterX) * NumCast(seldocview.layoutDoc._width),
-                NumCast(seldocview.rootDoc.rotateCenterY) * NumCast(seldocview.layoutDoc._height),
+                NumCast(seldocview.rootDoc.rotation_centerX) * NumCast(seldocview.layoutDoc._width),
+                NumCast(seldocview.rootDoc.rotation_centerY) * NumCast(seldocview.layoutDoc._height),
                 (NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI
             );
             return seldocview.props
@@ -746,8 +746,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
-        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || this._isRounding || this._isRotating;
-        const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
+        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
+        const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
         const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
         const hideOpenButton =
@@ -765,7 +765,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
-                return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return (docView.rootDoc.stayInCollection && !docView.rootDoc.timelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
 
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
@@ -871,7 +871,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
-                                {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
+                                {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as new embedding, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
                                 <>
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 84b2e1407..75e0e7c4c 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -46,14 +46,14 @@ export class FilterPanel extends React.Component<filterProps> {
     @computed get _allFacets() {
         // trace();
         const noviceReqFields = ['author', 'tags', 'text', 'type'];
-        const noviceLayoutFields: string[] = []; //["_curPage"];
+        const noviceLayoutFields: string[] = []; //["_layout_curPage"];
         const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
 
         const keys = new Set<string>(noviceFields);
         this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
         const sortedKeys = Array.from(keys.keys())
             .filter(key => key[0])
-            .filter(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
+            .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
             .sort();
         noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
         return [...noviceFields, ...sortedKeys];
@@ -88,7 +88,7 @@ export class FilterPanel extends React.Component<filterProps> {
                     (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal));
                     const fieldKey = Doc.LayoutFieldKey(t);
                     const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
-                    DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
+                    DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                 });
                 subDocs = newarray;
             }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index b04769c1e..141e99c66 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -102,7 +102,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 icon: 'clipboard',
                 pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()',
                 pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)',
-                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }),
+                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, isSystem: true }),
                 backgroundColor: 'orange',
             },
             { title: 'interpret text', icon: 'font', toolType: 'inktotext', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'orange' },
@@ -125,7 +125,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
                 backgroundColor: data.backgroundColor,
                 dragFactory: data.dragFactory,
-                system: true,
+                isSystem: true,
             })
         );
     }
@@ -138,19 +138,19 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 ignoreClick: true,
                 _lockedPosition: true,
                 title: 'buttons',
-                _autoHeight: true,
+                _layout_autoHeight: true,
                 _yMargin: 5,
                 linearViewIsExpanded: true,
                 backgroundColor: 'white',
-                system: true,
+                isSystem: true,
             });
             thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
                 _width: 300,
                 _height: 25,
-                _autoHeight: true,
+                _layout_autoHeight: true,
                 linearViewIsExpanded: true,
                 flexDirection: 'column',
-                system: true,
+                isSystem: true,
             });
             userDoc.thumbDoc = thumbDoc;
         }
@@ -916,7 +916,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
 
     @computed get elements() {
         const selView = GestureOverlay.DownDocView;
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1);
+        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._freeform_scale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1);
         const rect = this._overlayRef.current?.getBoundingClientRect();
         const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true);
         B.left = B.left - width / 2;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 0f8b46dbe..b9b92dd2b 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -374,10 +374,10 @@ export class KeyManager {
                             if (count === docids.length) {
                                 const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d, true)).clone : d)));
                                 if (added.length) {
-                                    added.map(doc => (doc.context = targetDataDoc));
+                                    added.map(doc => (doc.embedContainer = targetDataDoc));
                                     undoBatch(() => {
                                         targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
-                                        targetDataDoc[fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
+                                        targetDataDoc[fieldKey + '_modificationDate'] = new DateField(new Date(Date.now()));
                                     })();
                                 }
                             }
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 156825f41..d28981e17 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -50,21 +50,21 @@ export class InkStrokeProperties {
                 if (!requireCurrPoint || this._currentPoint !== -1) {
                     const doc = inkView.rootDoc;
                     if (doc.type === DocumentType.INK && doc.width && doc.height) {
-                        const ink = Cast(doc.data, InkField)?.inkData;
+                        const ink = Cast(doc.stroke, InkField)?.inkData;
                         if (ink) {
                             const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
                             const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
-                            const ptsXscale = (NumCast(doc._width) - NumCast(doc.strokeWidth)) / (oldXrange.max - oldXrange.min || 1) || 1;
-                            const ptsYscale = (NumCast(doc._height) - NumCast(doc.strokeWidth)) / (oldYrange.max - oldYrange.min || 1) || 1;
-                            const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth));
+                            const ptsXscale = (NumCast(doc._width) - NumCast(doc.stroke_width)) / (oldXrange.max - oldXrange.min || 1) || 1;
+                            const ptsYscale = (NumCast(doc._height) - NumCast(doc.stroke_width)) / (oldYrange.max - oldYrange.min || 1) || 1;
+                            const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.stroke_width));
                             if (newPoints) {
                                 const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X));
                                 const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y));
-                                doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.strokeWidth);
-                                doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth);
+                                doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.stroke_width);
+                                doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.stroke_width);
                                 doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale;
                                 doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale;
-                                Doc.GetProto(doc).data = new InkField(newPoints);
+                                Doc.GetProto(doc).stroke = new InkField(newPoints);
                                 appliedFunc = true;
                             }
                         }
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 246b887a6..d6a6a64bf 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -172,7 +172,7 @@ export class InkTranscription extends React.Component {
             if (!selected) {
                 return;
             }
-            const ctx = await Cast(selected[0].context, Doc);
+            const ctx = await Cast(selected[0].embedContainer, Doc);
             if (!ctx) {
                 return;
             }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 2fd6cc4d6..14c5dc58d 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -6,7 +6,7 @@
               point 1, tangent pt 1, tangent pt 2, point 2, point 3, tangent pt 3, ...  (Note that segment endpoints are duplicated ie Point2 = Point 3)
         brokenIndices - an array of indexes into the data field where the incoming and outgoing tangents are not constrained to be equal
         text - a text field that will be centered within a closed ink stroke
-        isInkMask - a flag that makes the ink stroke render as a mask over its collection where the stroke itself is mixBlendMode multiplied by 
+        stroke_isInkMask - a flag that makes the ink stroke render as a mask over its collection where the stroke itself is mixBlendMode multiplied by 
                the underlying collection content, and everything outside the stroke is covered by a semi-opaque dark gray mask.
 
     The coordinates of the ink data need to be mapped to the screen since ink points are not changed when the DocumentView is translated or scaled.
@@ -134,7 +134,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      *  dimmed by a semi-opaque overlay mask.
      */
     public static toggleMask = action((inkDoc: Doc) => {
-        inkDoc.isInkMask = !inkDoc.isInkMask;
+        inkDoc.stroke_isInkMask = !inkDoc.stroke_isInkMask;
     });
     @observable controlUndo: UndoManager.Batch | undefined;
     /**
@@ -241,7 +241,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     inkScaledData = () => {
         const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
-        const inkStrokeWidth = NumCast(this.rootDoc.strokeWidth, 1);
+        const inkStrokeWidth = NumCast(this.rootDoc.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;
@@ -310,8 +310,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             .map(p => ({ X: p[0], Y: p[1] }));
         const screenHdlPts = screenPts;
 
-        const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
-        const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
+        const startMarker = StrCast(this.layoutDoc.stroke_startMarker);
+        const endMarker = StrCast(this.layoutDoc.stroke_endMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale);
         return SnappingManager.GetIsDragging() ? null : !InkStrokeProperties.Instance._controlButton ? (
             !this.props.isSelected() || InkingStroke.IsClosed(inkData) ? null : (
@@ -330,12 +330,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     screenSpaceCenterlineStrokeWidth,
                     StrCast(inkDoc.strokeLineJoin),
                     StrCast(this.layoutDoc.strokeLineCap),
-                    StrCast(inkDoc.strokeBezier),
+                    StrCast(inkDoc.stroke_bezier),
                     'none',
                     startMarker,
                     endMarker,
                     markerScale * Math.min(screenSpaceCenterlineStrokeWidth, screenInkWidth[0] / screenSpaceCenterlineStrokeWidth),
-                    StrCast(inkDoc.strokeDash),
+                    StrCast(inkDoc.stroke_dash),
                     1,
                     1,
                     '',
@@ -355,11 +355,11 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         TraceMobx();
         const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData();
 
-        const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
-        const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
+        const startMarker = StrCast(this.layoutDoc.stroke_startMarker);
+        const endMarker = StrCast(this.layoutDoc.stroke_endMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1);
         const closed = InkingStroke.IsClosed(inkData);
-        const isInkMask = BoolCast(this.layoutDoc.isInkMask);
+        const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
         const fillColor = isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
         const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
 
@@ -381,12 +381,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkStrokeWidth,
             StrCast(this.layoutDoc.strokeLineJoin),
             StrCast(this.layoutDoc.strokeLineCap),
-            StrCast(this.layoutDoc.strokeBezier),
+            StrCast(this.layoutDoc.stroke_bezier),
             !closed ? 'none' : fillColor === 'transparent' ? 'none' : fillColor,
             startMarker,
             endMarker,
             markerScale,
-            StrCast(this.layoutDoc.strokeDash),
+            StrCast(this.layoutDoc.stroke_dash),
             inkScaleX,
             inkScaleY,
             '',
@@ -408,7 +408,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 inkStrokeWidth + (fillColor ? (closed ? 2 : (highlightIndex ?? 0) + 2) : 2),
                 StrCast(this.layoutDoc.strokeLineJoin),
                 StrCast(this.layoutDoc.strokeLineCap),
-                StrCast(this.layoutDoc.strokeBezier),
+                StrCast(this.layoutDoc.stroke_bezier),
                 !closed ? 'none' : !isInkMask && (fillColor === 'transparent' || suppressFill) ? 'none' : fillColor,
                 '',
                 '',
@@ -444,7 +444,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 <svg
                     className="inkStroke"
                     style={{
-                        transform: this.props.Document.isInkMask ? `rotate(-${NumCast(this.props.CollectionFreeFormDocumentView?.().Rot)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                        transform: isInkMask ? `rotate(-${NumCast(this.props.CollectionFreeFormDocumentView?.().Rot)}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,
                     }}
@@ -469,7 +469,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                             setContentView={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
                             yPadding={10}
                             xPadding={10}
-                            fieldKey={'text'}
+                            fieldKey="text"
                             fontSize={fsize}
                             dontRegisterView={true}
                             noSidebar={true}
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index c18a89481..1b309a6be 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -31,7 +31,7 @@ type LightboxSavedState = {
     panY: Opt<number>;
     scale: Opt<number>;
     scrollTop: Opt<number>;
-    layoutKey: Opt<string>;
+    layout_fieldKey: Opt<string>;
 };
 @observer
 export class LightboxView extends React.Component<LightboxViewProps> {
@@ -51,11 +51,11 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<LightboxSavedState> }[] = [];
     @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) {
         if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
-            if (this._savedState.panX !== undefined) this.LightboxDoc._panX = this._savedState.panX;
-            if (this._savedState.panY !== undefined) this.LightboxDoc._panY = this._savedState.panY;
-            if (this._savedState.scrollTop !== undefined) this.LightboxDoc._scrollTop = this._savedState.scrollTop;
-            if (this._savedState.scale !== undefined) this.LightboxDoc._viewScale = this._savedState.scale;
-            this.LightboxDoc.layoutKey = this._savedState.layoutKey;
+            if (this._savedState.panX !== undefined) this.LightboxDoc._freeform_panX = this._savedState.panX;
+            if (this._savedState.panY !== undefined) this.LightboxDoc._freeform_panY = this._savedState.panY;
+            if (this._savedState.scrollTop !== undefined) this.LightboxDoc._layout_scrollTop = this._savedState.scrollTop;
+            if (this._savedState.scale !== undefined) this.LightboxDoc._freeform_scale = this._savedState.scale;
+            this.LightboxDoc.layout_fieldKey = this._savedState.layout_fieldKey;
         }
         if (!doc) {
             this._docFilters && (this._docFilters.length = 0);
@@ -64,17 +64,17 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             MainView.Instance._exploreMode = false;
         } else {
             const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement();
-            l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
+            l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen');
             CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.());
             //TabDocView.PinDoc(doc, { hidePresBox: true });
             this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]);
             if (doc !== LightboxView.LightboxDoc) {
                 this._savedState = {
-                    layoutKey: StrCast(doc.layoutKey),
-                    panX: Cast(doc.panX, 'number', null),
-                    panY: Cast(doc.panY, 'number', null),
-                    scale: Cast(doc.viewScale, 'number', null),
-                    scrollTop: Cast(doc.scrollTop, 'number', null),
+                    layout_fieldKey: StrCast(doc.layout_fieldKey),
+                    panX: Cast(doc.freeform_panX, 'number', null),
+                    panY: Cast(doc.freeform_panY, 'number', null),
+                    scale: Cast(doc.freeform_scale, 'number', null),
+                    scrollTop: Cast(doc.layout_scrollTop, 'number', null),
                 };
             }
         }
@@ -91,7 +91,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         this._doc = doc;
         this._layoutTemplate = layoutTemplate instanceof Doc ? layoutTemplate : undefined;
         if (doc && (typeof layoutTemplate === 'string' ? layoutTemplate : undefined)) {
-            doc.layoutKey = layoutTemplate;
+            doc.layout_fieldKey = layoutTemplate;
         }
         this._docTarget = target || doc;
 
@@ -142,7 +142,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         return LightboxView.SetLightboxDoc(
             doc,
             undefined,
-            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort(
+            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort(
                 (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)
             ),
             layoutTemplate
@@ -156,17 +156,17 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
         if (targetDocView && target) {
             const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement();
-            l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
+            l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen');
             DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 });
             if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
         } else {
             if (!target && LightboxView.path.length) {
                 const saved = LightboxView._savedState;
                 if (LightboxView.LightboxDoc && saved) {
-                    LightboxView.LightboxDoc._panX = saved.panX;
-                    LightboxView.LightboxDoc._panY = saved.panY;
-                    LightboxView.LightboxDoc._viewScale = saved.scale;
-                    LightboxView.LightboxDoc._scrollTop = saved.scrollTop;
+                    LightboxView.LightboxDoc._freeform_panX = saved.panX;
+                    LightboxView.LightboxDoc._freeform_panY = saved.panY;
+                    LightboxView.LightboxDoc._freeform_scale = saved.scale;
+                    LightboxView.LightboxDoc._layout_scrollTop = saved.scrollTop;
                 }
                 const pop = LightboxView.path.pop();
                 if (pop) {
@@ -210,7 +210,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         const coll = LightboxView._docTarget;
         if (coll) {
             const fieldKey = Doc.LayoutFieldKey(coll);
-            const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
+            const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '_annotations'])];
             const links = LinkManager.Links(coll)
                 .map(link => LinkManager.getOppositeAnchor(link, coll))
                 .filter(doc => doc)
@@ -305,9 +305,9 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     title="toggle fit width"
                     onClick={e => {
                         e.stopPropagation();
-                        LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth;
+                        LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
                     }}>
-                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
+                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
                 </div>
                 <div
                     className="lightboxView-tabBtn"
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5af9a9f9a..ce29b63cd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -157,26 +157,26 @@ export class MainView extends React.Component {
                 'dataTransition',
                 'viewTransition',
                 'treeViewOpen',
-                'showSidebar',
+                'layout_showSidebar',
                 'itemIndex', // for changing slides in presentations
-                'sidebarWidthPercent',
-                'currentTimecode',
-                'timelineHeightPercent',
+                'layout_sidebarWidthPercent',
+                'layout_currentTimecode',
+                'layout_timelineHeightPercent',
                 'presStatus',
-                'panX',
-                'panY',
+                'freeform_panX',
+                'freeform_panY',
                 'overlayX',
                 'overlayY',
-                'fitWidth',
+                'layout_fitWidth',
                 'nativeWidth',
                 'nativeHeight',
-                'text-scrollHeight',
-                'text-height',
-                'hideMinimap',
-                'viewScale',
-                'scrollTop',
+                'text_scrollHeight',
+                'text_height',
+                'layout_hideMinimap',
+                'freeform_scale',
+                'layout_scrollTop',
                 'hidden',
-                'curPage',
+                'layout_curPage',
                 'viewType',
                 'chromeHidden',
                 'currentFrame',
@@ -880,8 +880,8 @@ export class MainView extends React.Component {
                 <CollectionLinearView
                     Document={Doc.MyDockedBtns}
                     DataDoc={undefined}
-                    fieldKey={'data'}
-                    dropAction={'alias'}
+                    fieldKey="data"
+                    dropAction="embed"
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
@@ -911,7 +911,7 @@ export class MainView extends React.Component {
         );
     }
     @computed get snapLines() {
-        return !SelectionManager.Views().some(dv => dv.rootDoc.showSnapLines) ? null : (
+        return !SelectionManager.Views().some(dv => dv.rootDoc.freeform_snapLines) ? null : (
             <div className="mainView-snapLines">
                 <svg style={{ width: '100%', height: '100%' }}>
                     {SnappingManager.horizSnapLines().map(l => (
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 556d9b6a0..eb0fd0c7a 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -123,9 +123,9 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                   DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                       dragComplete: e => {
                           if (!e.aborted && e.linkDocument) {
-                              Doc.GetProto(e.linkDocument).linkRelationship = 'cropped image';
+                              Doc.GetProto(e.linkDocument).link_relationship = 'cropped image';
                               Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title;
-                              Doc.GetProto(e.linkDocument).linkDisplay = false;
+                              Doc.GetProto(e.linkDocument).layout_linkDisplay = false;
                           }
                       },
                   });
@@ -153,10 +153,10 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                 annotationOn: this.props.rootDoc,
                 title: 'Annotation on ' + this.props.rootDoc.title,
             });
-            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
-            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1) + NumCast(this.props.scrollTop);
-            marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
-            marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
+            marqueeAnno.x = NumCast(this.props.docView.props.Document.freeform_panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
+            marqueeAnno.y = NumCast(this.props.docView.props.Document.freeform_panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1) + NumCast(this.props.scrollTop);
+            marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
+            marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
             anno.remove();
             savedAnnoMap.clear();
             return marqueeAnno;
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index c7a603897..f7fb443c0 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -57,7 +57,7 @@ export class PreviewCursor extends React.Component<{}> {
                                 title: url,
                                 _width: 500,
                                 _height: 300,
-                                useCors: true,
+                                data_useCors: true,
                                 x: newPoint[0],
                                 y: newPoint[1],
                             })
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index cf808f801..86230ca78 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -74,7 +74,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get maskButton() {
         return this.propertyToggleBtn(
             'Mask',
-            'isInkMask',
+            'stroke_isInkMask',
             on => (on ? 'Make plain ink' : 'Make highlight mask'),
             on => 'paint-brush',
             (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
@@ -91,7 +91,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get clustersButton() {
         return this.propertyToggleBtn(
             'Clusters',
-            '_useClusters',
+            '_freeform_useClusters',
             on => `${on ? 'Hide' : 'Show'} clusters`,
             on => 'braille'
         );
@@ -115,7 +115,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get fitContentButton() {
         return this.propertyToggleBtn(
             'View All',
-            '_fitContentsToBox',
+            '_freeform_fitContentsToBox',
             on => `${on ? "Don't" : 'Do'} fit content to container visible area`,
             on => 'object-group'
         );
@@ -136,20 +136,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     // containerDoc.noShadow =
                     // containerDoc.disableDocBrushing =
                     // containerDoc._forceActive =
-                    //containerDoc._fitContentsToBox =
+                    //containerDoc._freeform_fitContentsToBox =
                     containerDoc._isLightbox = !containerDoc._isLightbox;
                     //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
                     const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
                     //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
-                    containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.linkDisplay = false)));
+                    containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
                 });
             }
         );
     }
-    @computed get fitWidthButton() {
+    @computed get layout_fitWidthButton() {
         return this.propertyToggleBtn(
             'Fit\xA0Width',
-            '_fitWidth',
+            '_layout_fitWidth',
             on => `${on ? "Don't" : 'Do'} fit content to width of container`,
             on => 'arrows-alt-h'
         );
@@ -157,10 +157,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get captionButton() {
         return this.propertyToggleBtn(
             'Caption',
-            '_showCaption',
+            '_layout_showCaption',
             on => `${on ? 'Hide' : 'Show'} caption footer`,
             on => 'closed-captioning',
-            (dv, doc) => ((dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? 'caption' : undefined)
+            (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined)
         );
     }
     @computed get chromeButton() {
@@ -175,20 +175,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get titleButton() {
         return this.propertyToggleBtn(
             'Title',
-            '_showTitle',
+            '_layout_showTitle',
             on => 'Switch between title styles',
             on => 'text-width',
             (dv, doc) => {
                 const tdoc = dv?.rootDoc || doc;
-                const newtitle = !tdoc._showTitle ? 'title' : tdoc._showTitle === 'title' ? 'title:hover' : '';
-                tdoc._showTitle = newtitle;
+                const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : '';
+                tdoc._layout_showTitle = newtitle;
             }
         );
     }
-    @computed get autoHeightButton() {
+    @computed get layout_autoHeightButton() {
         return this.propertyToggleBtn(
             'Auto\xA0Size',
-            '_autoHeight',
+            '_layout_autoHeight',
             on => `Automatical vertical sizing to show all content`,
             on => 'arrows-alt-v'
         );
@@ -196,7 +196,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get gridButton() {
         return this.propertyToggleBtn(
             'Grid',
-            '_backgroundGridShow',
+            '_freeform_backgroundGrid',
             on => `Display background grid in collection`,
             on => 'border-all'
         );
@@ -236,7 +236,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get snapButton() {
         return this.propertyToggleBtn(
             'Snap\xA0Lines',
-            'showSnapLines',
+            'freeform_snapLines',
             on => `Display snapping lines when objects are dragged`,
             on => 'th',
             undefined,
@@ -412,12 +412,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.captionButton)}
                 {toggle(this.lockButton)}
                 {toggle(this.onClickButton)}
-                {toggle(this.fitWidthButton)}
+                {toggle(this.layout_fitWidthButton)}
                 {toggle(this.freezeThumb)}
                 {toggle(this.forceActiveButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
-                {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
+                {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
                 {toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
                 {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })}
                 {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 935d551c6..46e6fd188 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -25,7 +25,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
 
         if (otherdoc) {
             otherdoc.hidden = false;
-            this.props.addDocTab(Doc.IsPrototype(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, OpenWhere.toggleRight);
+            this.props.addDocTab(Doc.IsDocDataProto(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, OpenWhere.toggleRight);
         }
     };
 
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index c14d7ef73..12a09196e 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -23,14 +23,14 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
         if (!this.props.DocView) return [];
         const target = this.props.DocView.props.Document;
         const targetContext = this.props.DocView.props.docViewPath().lastElement()?.rootDoc;
-        const aliases = DocListCast(target.aliases);
-        const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
-        const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases));
+        const embeddings = DocListCast(target.proto_embeddings);
+        const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
+        const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings));
         const containers = containerSets.reduce((p, set) => {
             set.map(s => p.add(s));
             return p;
         }, new Set<Doc>());
-        const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.aliases));
+        const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.proto_embeddings));
         const doclayouts = Array.from(
             doclayoutSets
                 .reduce((p, set) => {
@@ -48,7 +48,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
 
     getOnClick = (col: Doc, target: Doc) => {
         if (!this.props.DocView) return;
-        col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
+        col = Doc.IsDocDataProto(col) ? Doc.MakeDelegate(col) : col;
         DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col);
     };
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 0b14c7b10..19c138a21 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -123,7 +123,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     docWidth = () => {
         if (this.selectedDoc) {
             const layoutDoc = this.selectedDoc;
-            const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._fitWidth);
+            const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._layout_fitWidth);
             if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.width - 20));
             return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20;
         } else {
@@ -141,7 +141,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     this.MAX_EMBED_HEIGHT,
                     Doc.NativeAspect(layoutDoc, undefined, true)
                         ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true)
-                        : layoutDoc._fitWidth
+                        : layoutDoc._layout_fitWidth
                         ? !Doc.NativeHeight(this.dataDoc)
                             ? NumCast(this.props.height)
                             : Math.min((this.docWidth() * Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))
@@ -199,8 +199,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get noviceFields() {
-        const noviceReqFields = ['author', 'creationDate', 'tags', '_curPage'];
-        return this.editableFields(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields);
+        const noviceReqFields = ['author', 'creationDate', 'tags', '_layout_curPage'];
+        return this.editableFields(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields);
     }
 
     @undoBatch
@@ -576,7 +576,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === 'up' ? 10 : -10));
                 break;
             case 'stk':
-                this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === 'up' ? 0.1 : -0.1));
+                this.selectedDoc && (this.selectedDoc.stroke_width = NumCast(this.selectedDoc?.stroke_width) + (dirs === 'up' ? 0.1 : -0.1));
                 break;
             case 'wid':
                 const oldWidth = NumCast(this.selectedDoc?._width);
@@ -813,25 +813,25 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get solidStk() {
-        return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === '0') ? true : false;
+        return this.selectedDoc?.color && (!this.selectedDoc?.stroke_dash || this.selectedDoc?.stroke_dash === '0') ? true : false;
     }
     @computed get dashdStk() {
-        return this.selectedDoc?.strokeDash || '';
+        return this.selectedDoc?.stroke_dash || '';
     }
     @computed get unStrokd() {
         return this.selectedDoc?.color ? true : false;
     }
     @computed get widthStk() {
-        return this.getField('strokeWidth') || '1';
+        return this.getField('stroke_width') || '1';
     }
     @computed get markScal() {
         return Number(this.getField('strokeMakerScale') || '1');
     }
     @computed get markHead() {
-        return this.getField('strokeStartMarker') || '';
+        return this.getField('stroke_startMarker') || '';
     }
     @computed get markTail() {
-        return this.getField('strokeEndMarker') || '';
+        return this.getField('stroke_endMarker') || '';
     }
     set solidStk(value) {
         this.dashdStk = '';
@@ -839,22 +839,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
     set dashdStk(value) {
         value && (this._lastDash = value) && (this.unStrokd = false);
-        this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined);
+        this.selectedDoc && (this.selectedDoc.stroke_dash = value ? this._lastDash : undefined);
     }
     set markScal(value) {
         this.selectedDoc && (this.selectedDoc.strokeMarkerScale = Number(value));
     }
     set widthStk(value) {
-        this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value));
+        this.selectedDoc && (this.selectedDoc.stroke_width = Number(value));
     }
     set unStrokd(value) {
         this.colorStk = value ? '' : this._lastLine;
     }
     set markHead(value) {
-        this.selectedDoc && (this.selectedDoc.strokeStartMarker = value);
+        this.selectedDoc && (this.selectedDoc.stroke_startMarker = value);
     }
     set markTail(value) {
-        this.selectedDoc && (this.selectedDoc.strokeEndMarker = value);
+        this.selectedDoc && (this.selectedDoc.stroke_endMarker = value);
     }
 
     @computed get stkInput() {
@@ -1151,16 +1151,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get description() {
-        return Field.toString(LinkManager.currentLink?.description as any as Field);
+        return Field.toString(LinkManager.currentLink?.link_description as any as Field);
     }
     @computed get relationship() {
-        return StrCast(LinkManager.currentLink?.linkRelationship);
+        return StrCast(LinkManager.currentLink?.link_relationship);
     }
     @observable private relationshipButtonColor: string = '';
 
     // @action
-    // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; }
-    // handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.relationship = e.target.value; }
+    // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.link_description = e.target.value; }
+    // handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.link_relationship = e.target.value; }
 
     @undoBatch
     handleDescriptionChange = action((value: string) => {
@@ -1171,9 +1171,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     });
 
     @undoBatch
-    handleLinkRelationshipChange = action((value: string) => {
+    handlelinkRelationshipChange = action((value: string) => {
         if (LinkManager.currentLink && this.selectedDoc) {
-            this.setLinkRelationshipValue(value);
+            this.setlinkRelationshipValue(value);
             return true;
         }
     });
@@ -1181,19 +1181,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @undoBatch
     setDescripValue = action((value: string) => {
         if (LinkManager.currentLink) {
-            Doc.GetProto(LinkManager.currentLink).description = value;
+            Doc.GetProto(LinkManager.currentLink).link_description = value;
             return true;
         }
     });
 
     @undoBatch
-    setLinkRelationshipValue = action((value: string) => {
+    setlinkRelationshipValue = action((value: string) => {
         if (LinkManager.currentLink) {
-            const prevRelationship = LinkManager.currentLink.linkRelationship as string;
-            LinkManager.currentLink.linkRelationship = value;
-            Doc.GetProto(LinkManager.currentLink).linkRelationship = value;
-            const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList);
-            const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
+            const prevRelationship = StrCast(LinkManager.currentLink.link_relationship);
+            LinkManager.currentLink.link_relationship = value;
+            Doc.GetProto(LinkManager.currentLink).link_relationship = value;
+            const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
+            const linkRelationshipSizes = NumListCast(Doc.UserDoc().link_relationshipSizes);
             const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
 
             // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
@@ -1264,13 +1264,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     onSelectOutRelationship = () => {
-        this.setLinkRelationshipValue(this.relationship);
+        this.setlinkRelationshipValue(this.relationship);
         document.getElementById('link_relationship_input')?.blur();
     };
 
     onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
         if (e.key === 'Enter') {
-            this.setLinkRelationshipValue(this.relationship);
+            this.setlinkRelationshipValue(this.relationship);
             document.getElementById('link_relationship_input')?.blur();
         }
     };
@@ -1283,7 +1283,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const ldoc = LinkManager.currentLink;
         const lanch = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor;
         if (ldoc && lanch) return LinkManager.getOppositeAnchor(ldoc, lanch) ?? lanch;
-        return ldoc ? DocCast(ldoc.anchor2) : ldoc;
+        return ldoc ? DocCast(ldoc.link_anchor_2) : ldoc;
     }
 
     @computed get sourceAnchor() {
@@ -1314,10 +1314,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             <input
                 autoComplete={'off'}
                 id="link_relationship_input"
-                value={StrCast(this.selectedDoc?.linkRelationship)}
+                value={StrCast(LinkManager.currentLink?.link_relationship)}
                 onKeyDown={this.onRelationshipKey}
                 onBlur={this.onSelectOutRelationship}
-                onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
+                onChange={e => this.handlelinkRelationshipChange(e.currentTarget.value)}
                 className="text"
                 type="text"
             />
@@ -1331,7 +1331,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 autoComplete="off"
                 style={{ textAlign: 'left' }}
                 id="link_description_input"
-                value={StrCast(this.selectedDoc?.description)}
+                value={Field.toString(LinkManager.currentLink?.link_description as any as Field)}
                 onKeyDown={this.onDescriptionKey}
                 onBlur={this.onSelectOutDesc}
                 onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
@@ -1406,8 +1406,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline">
                                         <p>Show link</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.linkDisplay ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'linkDisplay')}
+                                            style={{ background: !LinkManager.currentLink?.layout_linkDisplay ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_linkDisplay')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
@@ -1416,8 +1416,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
                                         <p>Auto-move anchors</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.linkAutoMove ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'linkAutoMove')}
+                                            style={{ background: !LinkManager.currentLink?.layout_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_autoMoveAnchors')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
@@ -1426,8 +1426,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
                                         <p>Display arrow</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.linkDisplayArrow ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'linkDisplayArrow')}
+                                            style={{ background: !LinkManager.currentLink?.layout_linkDisplayArrow ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_linkDisplayArrow')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index b0aab968e..741e87644 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -67,15 +67,15 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             annotationOn: this.props.rootDoc,
             _width: 200,
             _height: 50,
-            _fitWidth: true,
-            _autoHeight: true,
+            _layout_fitWidth: true,
+            _layout_autoHeight: true,
             _fontSize: StrCast(Doc.UserDoc().fontSize),
             _fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
-        const link = DocUtils.MakeLink(anchor, target, { linkRelationship: 'inline comment:comment on' });
-        link && (link.linkDisplay = false);
+        const link = DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' });
+        link && (link.layout_linkDisplay = false);
 
         const taggedContent = this.docFilters()
             .filter(data => data.split(':')[0])
@@ -151,7 +151,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     };
 
     get sidebarKey() {
-        return this.props.fieldKey + '-sidebar';
+        return this.props.fieldKey + '_sidebar';
     }
     filtersHeight = () => 38;
     screenToLocalTransform = () =>
@@ -170,11 +170,11 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
-    showTitle = () => 'title';
+    layout_showTitle = () => 'title';
     setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     sortByLinkAnchorY = (a: Doc, b: Doc) => {
-        const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].anchor1).y;
-        const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].anchor1).y;
+        const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].link_anchor_1).y;
+        const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].link_anchor_1).y;
         return NumCast(ay) - NumCast(by);
     };
     render() {
@@ -208,7 +208,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 style={{
                     position: 'absolute',
                     pointerEvents: this.props.isContentActive() ? 'all' : undefined,
-                    top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === 'title' ? 15 : 0,
+                    top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._layout_showTitle) === 'title' ? 15 : 0,
                     right: 0,
                     background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
                     width: `100%`,
@@ -235,7 +235,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         isAnnotationOverlay={false}
                         select={emptyFunction}
                         NativeDimScaling={returnOne}
-                        //childShowTitle={this.showTitle}
+                        //childlayout_showTitle={this.layout_showTitle}
                         isAnyChildContentActive={returnFalse}
                         childDocumentsActive={this.props.isContentActive}
                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 192a501f2..e852d9297 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -41,9 +41,9 @@ export enum StyleProp {
     PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified
     Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background
     HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that
-    ShowCaption = 'showCaption',
+    showCaption = 'layout_showCaption',
     TitleHeight = 'titleHeight', // Height of Title area
-    ShowTitle = 'showTitle', // whether to display a title on a Document (optional :hover suffix)
+    layout_showTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix)
     JitterRotation = 'jitterRotation', // whether documents should be randomly rotated
     BorderPath = 'customBorder', // border path for document view
     FontSize = 'fontSize', // size of text font
@@ -92,11 +92,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isAnnotated = property.includes(':annotated');
     const isOpen = property.includes(':open');
     const boxBackground = property.includes(':box');
-    const fieldKey = props?.fieldKey ? props.fieldKey + '-' : isCaption ? 'caption-' : '';
+    const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : '';
     const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
-    const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
+    const layout_showTitle = () => props?.styleProvider?.(doc, props, StyleProp.layout_showTitle);
     const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
     switch (property.split(':')[0]) {
         case StyleProp.TreeViewIcon:
@@ -138,25 +138,25 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.Opacity:
             return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : Cast(doc?._opacity, 'number', Cast(doc?.opacity, 'number', null));
         case StyleProp.HideLinkButton:
-            return props?.hideLinkButton || (!selected && doc?.hideLinkButton);
+            return props?.hideLinkButton || (!selected && doc?.layout_hideLinkButton);
         case StyleProp.FontSize:
             return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?._fontSize, StrCast(Doc.UserDoc().fontSize)));
         case StyleProp.FontFamily:
             return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._fontFamily, StrCast(Doc.UserDoc().fontFamily)));
         case StyleProp.FontWeight:
             return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._fontWeight, StrCast(Doc.UserDoc().fontWeight)));
-        case StyleProp.ShowTitle:
+        case StyleProp.layout_showTitle:
             return (
                 (doc &&
                     !doc.presentationTargetDoc &&
                     !props?.LayoutTemplateString?.includes(KeyValueBox.name) &&
-                    props?.showTitle?.() !== '' &&
+                    props?.layout_showTitle?.() !== '' &&
                     StrCast(
-                        doc._showTitle,
-                        props?.showTitle?.() ||
+                        doc._layout_showTitle,
+                        props?.layout_showTitle?.() ||
                             (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.FUNCPLOT, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any)
                                 ? doc.author === Doc.CurrentUserEmail
-                                    ? StrCast(Doc.UserDoc().showTitle)
+                                    ? StrCast(Doc.UserDoc().layout_showTitle)
                                     : remoteDocHeader
                                 : '')
                     )) ||
@@ -194,19 +194,19 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                   };
         case StyleProp.JitterRotation:
             return Doc.IsComicStyle(doc) ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
-        case StyleProp.ShowCaption:
-            return doc?._viewType === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._showCaption);
+        case StyleProp.showCaption:
+            return doc?._viewType === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._layout_showCaption);
         case StyleProp.HeaderMargin:
             return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) ||
-                (doc?.type === DocumentType.RTF && !showTitle()?.includes('noMargin')) ||
+                (doc?.type === DocumentType.RTF && !layout_showTitle()?.includes('noMargin')) ||
                 doc?.type === DocumentType.LABEL) &&
-                showTitle() &&
-                !StrCast(doc?.showTitle).includes(':hover')
+                layout_showTitle() &&
+                !StrCast(doc?.layout_showTitle).includes(':hover')
                 ? 15
                 : 0;
         case StyleProp.BackgroundColor: {
             if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
-            let docColor: Opt<string> = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
+            let docColor: Opt<string> = StrCast(doc?.[fieldKey + '_backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT:
                     docColor = docColor || (darkScheme() ? '' : '');
@@ -224,7 +224,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : 'rgba(105, 105, 105, 0.432)');
                     break;
                 case DocumentType.INK:
-                    docColor = doc?.isInkMask ? 'rgba(0,0,0,0.7)' : undefined;
+                    docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined;
                     break;
                 case DocumentType.SLIDER:
                     break;
@@ -293,7 +293,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 default:
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
-                        : props?.docViewPath().lastElement()?.rootDoc._useClusters
+                        : props?.docViewPath().lastElement()?.rootDoc._freeform_useClusters
                         ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1
                         ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
@@ -381,7 +381,7 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
         return doc._viewType === CollectionViewType.Docking ? null : (
             <>
                 {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => {
-                    DocFocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.context ?? doc?.annotationOn));
+                    DocFocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.embedContainer ?? doc?.annotationOn));
                 })}
             </>
         );
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 1c816aa05..814a95ab8 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -91,12 +91,12 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     }
     templateIsUsed = (selDoc: Doc, templateDoc: Doc) => {
         const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title);
-        return StrCast(selDoc.layoutKey) === 'layout_' + template ? 'check' : 'unchecked';
+        return StrCast(selDoc.layout_fieldKey) === 'layout_' + template ? 'check' : 'unchecked';
     };
     render() {
         TraceMobx();
         const firstDoc = this.props.docViews[0].props.Document;
-        const templateName = StrCast(firstDoc.layoutKey, 'layout').replace('layout_', '');
+        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);
         const templateMenu: Array<JSX.Element> = [];
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 7a393a4f7..adc97bbb4 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -76,7 +76,7 @@ export class Timeline extends React.Component<FieldViewProps> {
     private get children(): Doc[] {
         const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this.props.Document.type) as any);
         if (annotatedDoc) {
-            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + '-annotations']);
+            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + '_annotations']);
         }
         return DocListCast(this.props.Document[this.props.fieldKey]);
     }
@@ -290,15 +290,15 @@ export class Timeline extends React.Component<FieldViewProps> {
     };
 
     resetView(doc: Doc) {
-        doc._panX = doc._customOriginX ?? 0;
-        doc._panY = doc._customOriginY ?? 0;
-        doc._viewScale = doc._customOriginScale ?? 1;
+        doc._freeform_panX = doc._customOriginX ?? 0;
+        doc._freeform_panY = doc._customOriginY ?? 0;
+        doc._freeform_scale = doc._customOriginScale ?? 1;
     }
 
     setView(doc: Doc) {
-        doc._customOriginX = doc._panX;
-        doc._customOriginY = doc._panY;
-        doc._customOriginScale = doc._viewScale;
+        doc._customOriginX = doc._freeform_panX;
+        doc._customOriginY = doc._freeform_panY;
+        doc._customOriginScale = doc._freeform_scale;
     }
     /**
      * zooming mechanism (increment and spacing changes)
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 25c2e68e7..2349ba786 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -1,15 +1,15 @@
-import { action, computed, intercept, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc";
-import { Copy } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { ObjectField } from "../../../fields/ObjectField";
-import { listSpec } from "../../../fields/Schema";
-import { Cast, NumCast } from "../../../fields/Types";
-import { Transform } from "../../util/Transform";
-import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe";
-import "./Track.scss";
+import { action, computed, intercept, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, Opt, DocListCastAsync } from '../../../fields/Doc';
+import { Copy } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { listSpec } from '../../../fields/Schema';
+import { Cast, NumCast } from '../../../fields/Types';
+import { Transform } from '../../util/Transform';
+import { Keyframe, KeyframeFunc, RegionData } from './Keyframe';
+import './Track.scss';
 
 interface IProps {
     node: Doc;
@@ -32,27 +32,22 @@ export class Track extends React.Component<IProps> {
     @observable private _newKeyframe: boolean = false;
     private readonly MAX_TITLE_HEIGHT = 75;
     @observable private _trackHeight = 0;
-    private primitiveWhitelist = [
-        "x",
-        "y",
-        "_width",
-        "_height",
-        "opacity",
-        "_scrollTop"
-    ];
-    private objectWhitelist = [
-        "data"
-    ];
+    private primitiveWhitelist = ['x', 'y', '_width', '_height', 'opacity', '_layout_scrollTop'];
+    private objectWhitelist = ['data'];
 
-    @computed private get regions() { return DocListCast(this.props.node.regions); }
-    @computed private get time() { return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); }
+    @computed private get regions() {
+        return DocListCast(this.props.node.regions);
+    }
+    @computed private get time() {
+        return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
+    }
 
     async componentDidMount() {
         const regions = await DocListCastAsync(this.props.node.regions);
         if (!regions) this.props.node.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff
-        //these two lines are exactly same from timeline.tsx 
+        //these two lines are exactly same from timeline.tsx
         const relativeHeight = window.innerHeight / 20;
-        runInAction(() => this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT); //for responsiveness
+        runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness
         this._timelineVisibleReaction = this.timelineVisibleReaction();
         this._currentBarXReaction = this.currentBarXReaction();
         if (DocListCast(this.props.node.regions).length === 0) this.createRegion(this.time);
@@ -71,7 +66,6 @@ export class Track extends React.Component<IProps> {
     }
     ////////////////////////////////
 
-
     getLastRegionTime = () => {
         let lastTime: number = 0;
         let lastRegion: Opt<Doc>;
@@ -83,11 +77,11 @@ export class Track extends React.Component<IProps> {
             }
         });
         return lastRegion ? lastTime + NumCast(lastRegion.duration) : 0;
-    }
+    };
 
     /**
      * keyframe save logic. Needs to be changed so it's more efficient
-     * 
+     *
      */
     @action
     saveKeyframe = async () => {
@@ -97,16 +91,18 @@ export class Track extends React.Component<IProps> {
         if (this._newKeyframe) {
             DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => {
                 this.copyDocDataToKeyFrame(kf);
-                kf.opacity = (index === 0 || index === 3) ? 0.1 : 1;
+                kf.opacity = index === 0 || index === 3 ? 0.1 : 1;
             });
             this._newKeyframe = false;
         }
         if (!kf) return;
-        if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades
+        if (kf.type === KeyframeFunc.KeyframeType.default) {
+            // only save for non-fades
             this.copyDocDataToKeyFrame(kf);
             const leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists
-            const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists 
-            if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades
+            const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists
+            if (leftkf?.type === KeyframeFunc.KeyframeType.fade) {
+                //replicating this keyframe to fades
                 const edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf);
                 edge && this.copyDocDataToKeyFrame(edge);
                 leftkf && this.copyDocDataToKeyFrame(leftkf);
@@ -124,8 +120,7 @@ export class Track extends React.Component<IProps> {
         keyframes[kfIndex] = kf;
         this.saveStateKf = undefined;
         this.saveStateRegion = undefined;
-    }
-
+    };
 
     /**
      * autocreates keyframe
@@ -136,23 +131,27 @@ export class Track extends React.Component<IProps> {
         intercept(this.props.node, change => {
             return change;
         });
-        return reaction(() => {
-            return [...this.primitiveWhitelist.map(key => this.props.node[key]), ...objects];
-        }, (changed, reaction) => {
-            //check for region 
-            const region = this.findRegion(this.time);
-            if (region !== undefined) { //if region at scrub time exist
-                const r = region as RegionData; //for some region is returning undefined... which is not the case
-                if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot 
-                    this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
+        return reaction(
+            () => {
+                return [...this.primitiveWhitelist.map(key => this.props.node[key]), ...objects];
+            },
+            (changed, reaction) => {
+                //check for region
+                const region = this.findRegion(this.time);
+                if (region !== undefined) {
+                    //if region at scrub time exist
+                    const r = region as RegionData; //for some region is returning undefined... which is not the case
+                    if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) {
+                        //basically when there is no additional keyframe at that timespot
+                        this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
+                    }
                 }
-            }
-        }, { fireImmediately: false });
-    }
-
-
+            },
+            { fireImmediately: false }
+        );
+    };
 
-    // @observable private _storedState:(Doc | undefined) = undefined; 
+    // @observable private _storedState:(Doc | undefined) = undefined;
     // /**
     //  * reverting back to previous state before editing on AT
     //  */
@@ -161,54 +160,62 @@ export class Track extends React.Component<IProps> {
     //     if (this._storedState) this.applyKeys(this._storedState);
     // }
 
-
     /**
      * Reaction when scrubber bar changes
      * made into function so it's easier to dispose later
-    */
+     */
     @action
     currentBarXReaction = () => {
-        return reaction(() => this.props.currentBarX, () => {
-            const regiondata = this.findRegion(this.time);
-            if (regiondata) {
-                this.props.node.hidden = false;
-                // if (!this._autoKfReaction) {
-                //     // this._autoKfReaction = this.autoCreateKeyframe(); 
-                // }
-                this.timeChange();
-            } else {
-                this.props.node.hidden = true;
-                this.props.node.opacity = 0;
-                //if (this._autoKfReaction) this._autoKfReaction(); 
+        return reaction(
+            () => this.props.currentBarX,
+            () => {
+                const regiondata = this.findRegion(this.time);
+                if (regiondata) {
+                    this.props.node.hidden = false;
+                    // if (!this._autoKfReaction) {
+                    //     // this._autoKfReaction = this.autoCreateKeyframe();
+                    // }
+                    this.timeChange();
+                } else {
+                    this.props.node.hidden = true;
+                    this.props.node.opacity = 0;
+                    //if (this._autoKfReaction) this._autoKfReaction();
+                }
             }
-        });
-    }
+        );
+    };
 
     /**
      * when timeline is visible, reaction is ran so states are reverted
      */
     @action
     timelineVisibleReaction = () => {
-        return reaction(() => {
-            return this.props.timelineVisible;
-        }, isVisible => {
-            if (isVisible) {
-                this.regions.filter(region => !region.hasData).forEach(region => {
-                    for (let i = 0; i < 4; i++) {
-                        this.copyDocDataToKeyFrame(DocListCast(region.keyframes)[i]);
-                        if (i === 0 || i === 3) { //manually inputing fades
-                            DocListCast(region.keyframes)[i].opacity = 0.1;
-                        }
-                    }
-                });
-            } else {
-                //this.revertState(); 
+        return reaction(
+            () => {
+                return this.props.timelineVisible;
+            },
+            isVisible => {
+                if (isVisible) {
+                    this.regions
+                        .filter(region => !region.hasData)
+                        .forEach(region => {
+                            for (let i = 0; i < 4; i++) {
+                                this.copyDocDataToKeyFrame(DocListCast(region.keyframes)[i]);
+                                if (i === 0 || i === 3) {
+                                    //manually inputing fades
+                                    DocListCast(region.keyframes)[i].opacity = 0.1;
+                                }
+                            }
+                        });
+                } else {
+                    //this.revertState();
+                }
             }
-        });
-    }
+        );
+    };
 
-    @observable private saveStateKf: (Doc | undefined) = undefined;
-    @observable private saveStateRegion: (Doc | undefined) = undefined;
+    @observable private saveStateKf: Doc | undefined = undefined;
+    @observable private saveStateRegion: Doc | undefined = undefined;
 
     /**w
      * when scrubber position changes. Need to edit the logic
@@ -222,9 +229,9 @@ export class Track extends React.Component<IProps> {
         }
         const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on
         if (regiondata) {
-            const leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
-            const rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists        
-            const currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
+            const leftkf: Doc | undefined = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
+            const rightkf: Doc | undefined = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists
+            const currentkf: Doc | undefined = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
             if (currentkf) {
                 await this.applyKeys(currentkf);
                 this.saveStateKf = currentkf;
@@ -233,10 +240,10 @@ export class Track extends React.Component<IProps> {
                 await this.interpolate(leftkf, rightkf);
             }
         }
-    }
+    };
 
     /**
-     * applying changes (when saving the keyframe) 
+     * applying changes (when saving the keyframe)
      * need to change the logic here
      */
     @action
@@ -249,73 +256,73 @@ export class Track extends React.Component<IProps> {
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
-    }
-
+    };
 
     /**
      *  calculating current keyframe, if the scrubber is right on the keyframe
      */
     @action
     calcCurrent = (region: Doc) => {
-        let currentkf: (Doc | undefined) = undefined;
+        let currentkf: Doc | undefined = undefined;
         const keyframes = DocListCast(region.keyframes!);
-        keyframes.forEach((kf) => {
+        keyframes.forEach(kf => {
             if (NumCast(kf.time) === Math.round(this.time)) currentkf = kf;
         });
         return currentkf;
-    }
-
+    };
 
     /**
-     * basic linear interpolation function 
+     * basic linear interpolation function
      */
     @action
     interpolate = async (left: Doc, right: Doc) => {
         this.primitiveWhitelist.forEach(key => {
-            if (left[key] && right[key] && typeof (left[key]) === "number" && typeof (right[key]) === "number") { //if it is number, interpolate
+            if (left[key] && right[key] && typeof left[key] === 'number' && typeof right[key] === 'number') {
+                //if it is number, interpolate
                 const dif = NumCast(right[key]) - NumCast(left[key]);
                 const deltaLeft = this.time - NumCast(left.time);
                 const ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time));
-                this.props.node[key] = NumCast(left[key]) + (dif * ratio);
-            } else { // case data 
+                this.props.node[key] = NumCast(left[key]) + dif * ratio;
+            } else {
+                // case data
                 const stored = left[key];
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
-    }
+    };
 
     /**
      * finds region that corresponds to specific time (is there a region at this time?)
      * linear O(n) (maybe possible to optimize this with other Data structures?)
      */
     findRegion = (time: number) => {
-        return this.regions?.find(rd => (time >= NumCast(rd.position) && time <= (NumCast(rd.position) + NumCast(rd.duration))));
-    }
-
+        return this.regions?.find(rd => time >= NumCast(rd.position) && time <= NumCast(rd.position) + NumCast(rd.duration));
+    };
 
     /**
-     * double click on track. Signalling keyframe creation. 
+     * double click on track. Signalling keyframe creation.
      */
     @action
     onInnerDoubleClick = (e: React.MouseEvent) => {
         const inner = this._inner.current!;
         const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale);
-        this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement));
-    }
-
+        this.createRegion(KeyframeFunc.convertPixelTime(offsetX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
+    };
 
     /**
-     * creates a region (KEYFRAME.TSX stuff). 
+     * creates a region (KEYFRAME.TSX stuff).
      */
     @action
     createRegion = (time: number) => {
-        if (this.findRegion(time) === undefined) {  //check if there is a region where double clicking (prevents phantom regions)
+        if (this.findRegion(time) === undefined) {
+            //check if there is a region where double clicking (prevents phantom regions)
             const regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data
 
             regiondata.position = time; //set position
             const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions);
 
-            if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region
+            if (rightRegion && rightRegion.position - regiondata.position <= 4000) {
+                //edge case when there is less than default 4000 duration space between this and right region
                 regiondata.duration = rightRegion.position - regiondata.position;
             }
             if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) {
@@ -325,27 +332,30 @@ export class Track extends React.Component<IProps> {
                 return regiondata;
             }
         }
-    }
+    };
 
     @action
-    makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time 
+    makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => {
+        //Kfpos is mouse offsetX, representing time
         const trackKeyFrames = DocListCast(regiondata.keyframes);
         const existingkf = trackKeyFrames.find(TK => TK.time === time);
         if (existingkf) return existingkf;
-        //else creates a new doc. 
+        //else creates a new doc.
         const newKeyFrame: Doc = new Doc();
         newKeyFrame.time = time;
         newKeyFrame.type = type;
         this.copyDocDataToKeyFrame(newKeyFrame);
         //assuming there are already keyframes (for keeping keyframes in order, sorted by time)
         if (trackKeyFrames.length === 0) regiondata.keyframes!.push(newKeyFrame);
-        trackKeyFrames.map(kf => NumCast(kf.time)).forEach((kfTime, index) => {
-            if ((kfTime < time && index === trackKeyFrames.length - 1) || (kfTime < time && time < NumCast(trackKeyFrames[index + 1].time))) {
-                regiondata.keyframes!.splice(index + 1, 0, newKeyFrame);
-            }
-        });
+        trackKeyFrames
+            .map(kf => NumCast(kf.time))
+            .forEach((kfTime, index) => {
+                if ((kfTime < time && index === trackKeyFrames.length - 1) || (kfTime < time && time < NumCast(trackKeyFrames[index + 1].time))) {
+                    regiondata.keyframes!.splice(index + 1, 0, newKeyFrame);
+                }
+            });
         return newKeyFrame;
-    }
+    };
 
     @action
     copyDocDataToKeyFrame = (doc: Doc) => {
@@ -353,7 +363,7 @@ export class Track extends React.Component<IProps> {
             const originalVal = this.props.node[key];
             doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal;
         });
-    }
+    };
 
     /**
      * UI sstuff here. Not really much to change
@@ -362,10 +372,13 @@ export class Track extends React.Component<IProps> {
         return (
             <div className="track-container">
                 <div className="track">
-                    <div className="inner" ref={this._inner} style={{ height: `${this._trackHeight}px` }}
+                    <div
+                        className="inner"
+                        ref={this._inner}
+                        style={{ height: `${this._trackHeight}px` }}
                         onDoubleClick={this.onInnerDoubleClick}
                         onPointerOver={() => Doc.BrushDoc(this.props.node)}
-                        onPointerOut={() => Doc.UnBrushDoc(this.props.node)} >
+                        onPointerOut={() => Doc.UnBrushDoc(this.props.node)}>
                         {this.regions?.map((region, i) => {
                             return <Keyframe key={`${i}`} {...this.props} RegionData={region} makeKeyData={this.makeKeyData} />;
                         })}
@@ -374,4 +387,4 @@ export class Track extends React.Component<IProps> {
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 02f925bea..9ec78155c 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -41,16 +41,16 @@ export class CollectionCarouselView extends CollectionSubView() {
         const childValue = doc?.['caption-' + property] ? this.props.styleProvider?.(doc, captionProps, property) : undefined;
         return childValue ?? this.props.styleProvider?.(this.layoutDoc, captionProps, property);
     };
-    panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._showCaption) ? 50 : 0);
+    panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0);
     onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
     onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
     @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
         const curDoc = this.childLayoutPairs?.[index];
         const captionProps = { ...this.props, fieldKey: 'caption', setHeight: undefined };
-        const marginX = NumCast(this.layoutDoc['caption-xMargin']);
-        const marginY = NumCast(this.layoutDoc['caption-yMargin']);
-        const showCaptions = StrCast(this.layoutDoc._showCaption);
+        const marginX = NumCast(this.layoutDoc['caption_xMargin']);
+        const marginY = NumCast(this.layoutDoc['caption_yMargin']);
+        const show_captions = StrCast(this.layoutDoc._layout_showCaption);
         return !(curDoc?.layout instanceof Doc) ? null : (
             <>
                 <div className="collectionCarouselView-image" key="image">
@@ -60,7 +60,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                         NativeHeight={returnZero}
                         onDoubleClick={this.onContentDoubleClick}
                         onClick={this.onContentClick}
-                        hideCaptions={showCaptions ? true : false}
+                        hideCaptions={show_captions ? true : false}
                         renderDepth={this.props.renderDepth + 1}
                         LayoutTemplate={this.props.childLayoutTemplate}
                         LayoutTemplateString={this.props.childLayoutString}
@@ -75,13 +75,13 @@ export class CollectionCarouselView extends CollectionSubView() {
                     key="caption"
                     onWheel={StopEvent}
                     style={{
-                        display: showCaptions ? undefined : 'none',
+                        display: show_captions ? undefined : 'none',
                         borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding),
                         marginRight: marginX,
                         marginLeft: marginX,
                         width: `calc(100% - ${marginX * 2}px)`,
                     }}>
-                    <FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={showCaptions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
+                    <FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
                 </div>
             </>
         );
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index bb1f788d4..3a4691484 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -127,12 +127,12 @@ export class CollectionDockingView extends CollectionSubView() {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (instance) {
-            if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') {
+            if (doc._viewType === CollectionViewType.Docking && doc.layout_fieldKey === 'layout') {
                 return DashboardView.openDashboard(doc);
             }
             const newItemStackConfig = {
                 type: 'stack',
-                content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))],
+                content: [CollectionDockingView.makeDocumentConfig(Doc.MakeEmbedding(doc))],
             };
             const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
             instance._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -411,8 +411,8 @@ export class CollectionDockingView extends CollectionSubView() {
             const _height = Number(getComputedStyle(content).height.replace('px', ''));
             return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', (iconFile, _nativeWidth, _nativeHeight) => {
                 const proto = this.dataDoc; // Cast(img.proto, Doc, null)!;
-                proto['thumb-nativeWidth'] = _width;
-                proto['thumb-nativeHeight'] = _height;
+                proto['thumb_nativeWidth'] = _width;
+                proto['thumb_nativeHeight'] = _height;
                 proto.thumb = new ImageField(iconFile);
             });
         }
@@ -434,11 +434,11 @@ export class CollectionDockingView extends CollectionSubView() {
             .map(f => f as Doc);
         const newtabs = origtabs.map(origtab => {
             const origtabdocs = DocListCast(origtab.data);
-            const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeAlias(origtab);
-            const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeAlias(origtabdoc));
+            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);
-                newtabdocs.forEach(ntab => (ntab.context = newtab));
+                newtabdocs.forEach(ntab => (ntab.embedContainer = newtab));
             }
             json = json.replace(origtab[Id], newtab[Id]);
             return newtab;
@@ -498,8 +498,8 @@ export class CollectionDockingView extends CollectionSubView() {
                 const docToAdd = Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(),
                     _height: this.props.PanelHeight(),
-                    _backgroundGridShow: true,
-                    _fitWidth: true,
+                    _freeform_backgroundGrid: true,
+                    _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
@@ -541,8 +541,8 @@ export class CollectionDockingView extends CollectionSubView() {
                         const docToAdd = Docs.Create.FreeformDocument([], {
                             _width: this.props.PanelWidth(),
                             _height: this.props.PanelHeight(),
-                            _fitWidth: true,
-                            _backgroundGridShow: true,
+                            _layout_fitWidth: true,
+                            _freeform_backgroundGrid: true,
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
                         this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index befd89e41..528781991 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -44,7 +44,7 @@ interface CMVFieldRowProps {
 @observer
 export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowProps> {
     @observable private _background = 'inherit';
-    @observable private _createAliasSelected: boolean = false;
+    @observable private _createEmbeddingSelected: boolean = false;
     @observable private heading: string = '';
     @observable private color: string = '#f1efeb';
     @observable private collapsed: boolean = false;
@@ -95,7 +95,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @undoBatch
     rowDrop = action((e: Event, de: DragManager.DropEvent) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         if (de.complete.docDragData) {
             this.props.parent.Document.dropConverter instanceof ScriptField && this.props.parent.Document.dropConverter.script.run({ dragData: de.complete.docDragData });
             const key = this.props.pivotField;
@@ -117,12 +117,12 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         const key = this.props.pivotField;
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.parent.columnHeaders) {
-                if (this.props.parent.columnHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+            if (this.props.parent.colHeaderData) {
+                if (this.props.parent.colHeaderData.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
                     return false;
                 }
             }
@@ -135,7 +135,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     changeColumnColor = (color: string) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         this._color = color;
     };
 
@@ -143,16 +143,16 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     pointerLeaveRow = () => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         this._background = 'inherit';
     };
 
     @action
     addDocument = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         const key = this.props.pivotField;
-        const newDoc = Docs.Create.TextDocument('', { _autoHeight: true, _width: 200, _fitWidth: true, title: value });
+        const newDoc = Docs.Create.TextDocument('', { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
         const onLayoutDoc = this.onLayoutDoc(key);
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = value;
@@ -163,33 +163,33 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     deleteRow = undoBatch(
         action(() => {
-            this._createAliasSelected = false;
+            this._createEmbeddingSelected = false;
             const key = this.props.pivotField;
             this.props.docList.forEach(d => Doc.SetInPlace(d, key, undefined, true));
-            if (this.props.parent.columnHeaders && this.props.headingObject) {
-                const index = this.props.parent.columnHeaders.indexOf(this.props.headingObject);
-                this.props.parent.columnHeaders.splice(index, 1);
+            if (this.props.parent.colHeaderData && this.props.headingObject) {
+                const index = this.props.parent.colHeaderData.indexOf(this.props.headingObject);
+                this.props.parent.colHeaderData.splice(index, 1);
             }
         })
     );
 
     @action
     collapseSection = (e: any) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         this.toggleVisibility();
         e.stopPropagation();
     };
 
     headerMove = (e: PointerEvent) => {
-        const alias = Doc.MakeAlias(this.props.Document);
+        const embedding = Doc.MakeEmbedding(this.props.Document);
         const key = this.props.pivotField;
         let value = this.getValue(this.heading);
         value = typeof value === 'string' ? `"${value}"` : value;
         const script = `return doc.${key} === ${value}`;
         const compiled = CompileScript(script, { params: { doc: Doc.name } });
         if (compiled.compiled) {
-            alias.viewSpecScript = new ScriptField(compiled);
-            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
+            embedding.viewSpecScript = new ScriptField(compiled);
+            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
         }
         return true;
     };
@@ -198,7 +198,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         if (e.button === 0 && !e.ctrlKey) {
             setupMoveUpEvents(this, e, this.headerMove, emptyFunction, e => !this.props.chromeHidden && this.collapseSection(e));
-            this._createAliasSelected = false;
+            this._createEmbeddingSelected = false;
         }
     };
 
@@ -242,16 +242,16 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         );
     };
 
-    toggleAlias = action(() => (this._createAliasSelected = true));
+    toggleEmbedding = action(() => (this._createEmbeddingSelected = true));
     toggleVisibility = () => (this._collapsed = !this.collapsed);
 
     renderMenu = () => {
-        const selected = this._createAliasSelected;
+        const selected = this._createEmbeddingSelected;
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.toggleAlias}>
-                        Create Alias
+                    <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.toggleEmbedding}>
+                        Create Embedding
                     </div>
                     <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.deleteRow}>
                         Delete
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 2154016bd..615014c4a 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -108,8 +108,8 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
                 <CollectionLinearView
                     Document={selDoc}
                     DataDoc={undefined}
-                    fieldKey={'data'}
-                    dropAction={'alias'}
+                    fieldKey="data"
+                    dropAction="embed"
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
@@ -246,32 +246,32 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     _viewCommand = {
         params: ['target'],
         title: 'bookmark view',
-        script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
+        script: "self.target._freeform_panX = self['target-freeform_panX']; self.target._freeform_panY = self['target-freeform_panY']; self.target._freeform_scale = self['target_freeform_scale']; gotoFrame(self.target, self['target-currentFrame']);",
         immediate: undoBatch((source: Doc[]) => {
-            this.target._panX = 0;
-            this.target._panY = 0;
-            this.target._viewScale = 1;
+            this.target._freeform_panX = 0;
+            this.target._freeform_panY = 0;
+            this.target._freeform_scale = 1;
             this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0;
         }),
         initialize: (button: Doc) => {
-            button['target-panX'] = this.target._panX;
-            button['target-panY'] = this.target._panY;
-            button['target-viewScale'] = this.target._viewScale;
+            button['target-panX'] = this.target._freeform_panX;
+            button['target-panY'] = this.target._freeform_panY;
+            button['target-_ayout_scale'] = this.target._freeform_scale;
             button['target-currentFrame'] = this.target._currentFrame;
         },
     };
     _clusterCommand = {
         params: ['target'],
         title: 'fit content',
-        script: 'self.target._fitContentsToBox = !self.target._fitContentsToBox;',
-        immediate: undoBatch((source: Doc[]) => (this.target._fitContentsToBox = !this.target._fitContentsToBox)),
+        script: 'self.target._freeform_fitContentsToBox = !self.target._freeform_fitContentsToBox;',
+        immediate: undoBatch((source: Doc[]) => (this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox)),
         initialize: emptyFunction,
     };
     _fitContentCommand = {
         params: ['target'],
         title: 'toggle clusters',
-        script: 'self.target._useClusters = !self.target._useClusters;',
-        immediate: undoBatch((source: Doc[]) => (this.target._useClusters = !this.target._useClusters)),
+        script: 'self.target._freeform_useClusters = !self.target._freeform_useClusters;',
+        immediate: undoBatch((source: Doc[]) => (this.target._freeform_useClusters = !this.target._freeform_useClusters)),
         initialize: emptyFunction,
     };
     _saveFilterCommand = {
@@ -548,7 +548,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     @undoBatch
     @action
     startRecording = () => {
-        const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
+        const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _layout_fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
         //Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
         CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
     };
@@ -568,29 +568,29 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     }
 
     @undoBatch
-    onAlias = () => {
+    onEmbed = () => {
         if (this.selectedDoc && this.selectedDocumentView) {
             // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
             // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
             // copy.y = NumCast(this.selectedDoc.y) + 30;
             // this.selectedDocumentView.props.addDocument?.(copy);
-            const alias = Doc.MakeAlias(this.selectedDoc);
-            alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
-            alias.y = NumCast(this.selectedDoc.y) + 30;
-            this.selectedDocumentView.props.addDocument?.(alias);
+            const embedding = Doc.MakeEmbedding(this.selectedDoc);
+            embedding.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
+            embedding.y = NumCast(this.selectedDoc.y) + 30;
+            this.selectedDocumentView.props.addDocument?.(embedding);
         }
     };
-    onAliasButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+    onEmbedButtonDown = (e: React.PointerEvent): void => {
+        setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction);
     };
 
     @undoBatch
-    onAliasButtonMoved = (e: PointerEvent) => {
+    onEmbedButtonMoved = (e: PointerEvent) => {
         const contentDiv = this.selectedDocumentView?.ContentDiv;
         if (contentDiv && this.selectedDoc) {
             const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
             const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
-            dragData.defaultDropAction = 'alias';
+            dragData.defaultDropAction = 'embed';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
                 offsetX: offset[0],
@@ -603,11 +603,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     };
 
     @computed
-    get aliasButton() {
+    get embedButton() {
         const targetDoc = this.selectedDoc;
         return !targetDoc || targetDoc.type === DocumentType.PRES ? null : (
-            <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an alias'}</div>} placement="top">
-                <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: 'drag' }}>
+            <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an embedding'}</div>} placement="top">
+                <button className="antimodeMenu-button" onPointerDown={this.onEmbedButtonDown} onClick={this.onEmbed} style={{ cursor: 'drag' }}>
                     <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
                 </button>
             </Tooltip>
@@ -657,7 +657,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                     <div className="collectionViewBaseChrome">
                         {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
                         <div className="collectionMenu-divider" key="divider1"></div>
-                        {this.aliasButton}
+                        {this.embedButton}
                         {/* {this.pinButton} */}
                         {this.toggleOverlayButton}
                         <div className="collectionMenu-divider" key="divider2"></div>
@@ -691,7 +691,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
         return this.props.docView.props.Document;
     }
     @computed get dataField() {
-        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '-annotations' : '')];
+        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '_annotations' : '')];
     }
     @computed get childDocs() {
         return DocListCast(this.dataField);
@@ -798,7 +798,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                 if (doc.type === DocumentType.INK) {
                     switch (field) {
                         case 'width':
-                            doc.strokeWidth = Number(value);
+                            doc.stroke_width = Number(value);
                             break;
                         case 'color':
                             doc.color = String(value);
@@ -807,7 +807,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                             doc.fillColor = String(value);
                             break;
                         case 'dash':
-                            doc.strokeDash = value;
+                            doc.stroke_dash = value;
                     }
                 }
             })
@@ -1005,16 +1005,15 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
 
         if (Doc.noviceMode) {
             if (docs instanceof Doc) {
-                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
+                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
                 return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
-            } else {
-                const keys = new Set<string>();
-                docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-                const noviceKeys = Array.from(keys).filter(
-                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
-                );
-                return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
             }
+            const keys = new Set<string>();
+            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+            const noviceKeys = Array.from(keys).filter(
+                key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
+            );
+            return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
         }
 
         if (docs instanceof Doc) {
@@ -1125,13 +1124,13 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
 
         if (Doc.UserDoc().noviceMode) {
             if (docs instanceof Doc) {
-                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
+                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
                 return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
             } else {
                 const keys = new Set<string>();
                 docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
                 const noviceKeys = Array.from(keys).filter(
-                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
+                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
                 );
                 return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
             }
@@ -1235,9 +1234,9 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
         const dividerWidth = 4;
         const borderWidth = Number(COLLECTION_BORDER_WIDTH);
         const panelWidth = this.props.docView.props.PanelWidth();
-        const previewWidth = NumCast(this.document.schemaPreviewWidth);
+        const previewWidth = NumCast(this.document.schema_previewWidth);
         const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
-        this.document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
+        this.document.schema_previewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
     };
 
     @undoBatch
@@ -1254,7 +1253,7 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
     };
 
     render() {
-        const previewWidth = NumCast(this.document.schemaPreviewWidth);
+        const previewWidth = NumCast(this.document.schema_previewWidth);
         const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []).length > 0;
 
         return (
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 99d4d0bee..a65e23911 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -49,12 +49,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         return BoolCast(this.layoutDoc.chromeHidden);
     }
     // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
-    @computed get columnHeaders() {
-        const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
-        const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
-        if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
+    @computed get colHeaderData() {
+        const colHeaderData = Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+        const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !colHeaderData?.find(sh => sh.heading === 'unset'));
+        if (needsUnsetCategory || colHeaderData === undefined || colHeaderData.length === 0) {
             setTimeout(() => {
-                const columnHeaders = Array.from(Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null) ?? []);
+                const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
                 const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
                 if (needsUnsetCategory || columnHeaders.length === 0) {
                     columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
@@ -62,7 +62,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 }
             });
         }
-        return columnHeaders ?? ([] as SchemaHeaderField[]);
+        return colHeaderData ?? ([] as SchemaHeaderField[]);
     }
     @computed get headerMargin() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
@@ -78,7 +78,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     }
     // numGroupColumns returns the number of columns
     @computed get numGroupColumns() {
-        return this.columnHeaders.length;
+        return this.colHeaderData.length;
     }
     // PanelWidth returns the size of the total available space the view occupies
     @computed get PanelWidth() {
@@ -117,7 +117,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // (2) documentView gets unmounted as you remove it from the list
     @computed get Sections() {
         TraceMobx();
-        const columnHeaders = this.columnHeaders;
+        const columnHeaders = this.colHeaderData;
         // filter out the currently dragged docs from the child docs, since we will insert them later
         const docs = this.childDocs.filter(d => !DragManager.docsBeingDragged.includes(d));
         const sections = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
@@ -149,9 +149,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     componentDidMount() {
         super.componentDidMount?.();
         document.addEventListener('pointerup', this.removeDocDragHighlight, true);
-        this._disposers.autoHeight = reaction(
-            () => this.layoutDoc._autoHeight,
-            autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
+        this._disposers.layout_autoHeight = reaction(
+            () => this.layoutDoc._layout_autoHeight,
+            layout_autoHeight =>
+                layout_autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
         );
     }
 
@@ -230,7 +231,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 PanelHeight={height}
                 styleProvider={this.styleProvider}
                 docViewPath={this.props.docViewPath}
-                fitWidth={this.props.childFitWidth}
+                layout_fitWidth={this.props.childLayoutFitWidth}
                 isContentActive={emptyFunction}
                 onKey={this.onKeyDown}
                 //TODO: change this from a prop to a parameter passed into a function
@@ -238,12 +239,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this.props.childLayoutTemplate}
                 LayoutTemplateString={this.props.childLayoutString}
-                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
-                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
+                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
                 dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
                 dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
                 rootSelected={this.rootSelected}
-                showTitle={this.props.childShowTitle}
+                layout_showTitle={this.props.childlayout_showTitle}
                 dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
@@ -280,10 +281,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // if a note doc. Otherwise, returns the normal width (for graphs, images, etc...)
     getDocWidth(d: Doc) {
         const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
-        const existingHeader = this.columnHeaders.find(sh => sh.heading === heading);
+        const existingHeader = this.colHeaderData.find(sh => sh.heading === heading);
         const existingWidth = existingHeader?.width ? existingHeader.width : 0;
         const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
-        const width = d.fitWidth ? maxWidth : d[WidthSym]();
+        const width = d.layout_fitWidth ? maxWidth : d[WidthSym]();
         return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
     }
 
@@ -293,14 +294,14 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
-        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
-        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[WidthSym]() : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
             const docWid = this.getDocWidth(d);
             return Math.min(maxHeight, (docWid * nh) / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
@@ -318,7 +319,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const n = headers.length;
         const curWidths = headers.reduce((sum, hdr) => sum + Math.abs(hdr.width), 0);
         const scaleFactor = 1 / curWidths;
-        this.dataDoc.columnHeaders = new List<SchemaHeaderField>(
+        this.dataDoc[this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>(
             headers.map(h => {
                 h.setWidth(Math.abs(h.width) * scaleFactor);
                 return h;
@@ -352,7 +353,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             });
             // we alter the pivot fields of the docs in case they are moved to a new column.
             const colIndex = this.getColumnFromXCoord(xCoord);
-            const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
+            const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading);
             DragManager.docsBeingDragged.forEach(d => (d[this.notetakingCategoryField] = colHeader));
             // used to notify sections to re-render
             this.docsDraggedRowCol.length = 0;
@@ -365,12 +366,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // This function is used to know which document a column SHOULD be in while it is being dragged.
     getColumnFromXCoord = (xCoord: number): number | undefined => {
         let colIndex: number | undefined = undefined;
-        const numColumns = this.columnHeaders.length;
+        const numColumns = this.colHeaderData.length;
         const coords = [];
         let colStartXCoord = 0;
         for (let i = 0; i < numColumns; i++) {
             coords.push(colStartXCoord);
-            colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.DividerWidth;
+            colStartXCoord += this.colHeaderData[i].width * this.availableWidth + this.DividerWidth;
         }
         coords.push(this.PanelWidth);
         for (let i = 0; i < numColumns; i++) {
@@ -386,7 +387,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     getDocsFromXCoord = (xCoord: number): Doc[] => {
         const docsMatchingHeader: Doc[] = [];
         const colIndex = this.getColumnFromXCoord(xCoord);
-        const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
+        const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading);
         this.childDocs?.map(d => {
             if (d instanceof Promise) return;
             const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
@@ -401,7 +402,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.ctrlKey || docView.rootDoc._singleLine) && ['Enter'].includes(e.key)) {
+        if (docView && (e.ctrlKey || docView.rootDoc._createDocOnCR) && ['Enter'].includes(e.key)) {
             e.stopPropagation?.();
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             Doc.GetProto(newDoc).text = undefined;
@@ -436,10 +437,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                     }
                 }
             }
-        } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
-            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
+        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
         } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
@@ -468,7 +469,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                     this.onPointerMove(true, e.clientX, e.clientY);
                     docus?.map((doc: Doc) => this.addDocument(doc));
                     const newDoc = this.childDocs.lastElement();
-                    const colHeader = colInd === undefined ? 'unset' : StrCast(this.columnHeaders[colInd].heading);
+                    const colHeader = colInd === undefined ? 'unset' : StrCast(this.colHeaderData[colInd].heading);
                     newDoc[this.notetakingCategoryField] = colHeader;
                     const docs = this.childDocList;
                     if (docs && targInd !== -1) {
@@ -502,7 +503,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                         this.refList.push(ref);
                         this.observer = new _global.ResizeObserver(
                             action((entries: any) => {
-                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                                     const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
                                     if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
                                         this.props.setHeight?.(height);
@@ -516,7 +517,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 select={this.props.select}
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
-                columnHeaders={this.columnHeaders}
+                colHeaderData={this.colHeaderData}
                 Document={this.props.Document}
                 DataDoc={this.props.DataDoc}
                 resizeColumns={this.resizeColumns}
@@ -524,6 +525,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 numGroupColumns={this.numGroupColumns}
                 gridGap={this.gridGap}
                 pivotField={this.notetakingCategoryField}
+                fieldKey={this.fieldKey}
                 dividerWidth={this.DividerWidth}
                 maxColWidth={this.maxColWidth}
                 availableWidth={this.availableWidth}
@@ -546,15 +548,15 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @undoBatch
     @action
     addGroup = (value: string) => {
-        if (this.columnHeaders) {
-            for (const header of this.columnHeaders) {
+        if (this.colHeaderData) {
+            for (const header of this.colHeaderData) {
                 if (header.heading === value) {
                     alert('You cannot use an existing column name. Please try a new column name');
                     return value;
                 }
             }
         }
-        const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
+        const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         const newColWidth = 1 / (this.numGroupColumns + 1);
         columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
         value && this.resizeColumns(columnHeaders);
@@ -566,7 +568,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
             subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
-            subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+            subItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
             subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
             ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
         }
@@ -576,8 +578,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @action
     setColumnStartXCoords = (movementXScreen: number, colIndex: number) => {
         const movementX = this.props.ScreenToLocalTransform().transformDirection(movementXScreen, 0)[0];
-        const leftHeader = this.columnHeaders[colIndex];
-        const rightHeader = this.columnHeaders[colIndex + 1];
+        const leftHeader = this.colHeaderData[colIndex];
+        const rightHeader = this.colHeaderData[colIndex + 1];
         leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
         rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
     };
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 28bdd0cb9..63becac1e 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -29,8 +29,9 @@ interface CSVFieldColumnProps {
     docList: Doc[];
     heading: string;
     pivotField: string;
+    fieldKey: string | undefined;
     chromeHidden?: boolean;
-    columnHeaders: SchemaHeaderField[] | undefined;
+    colHeaderData: SchemaHeaderField[] | undefined;
     headingObject: SchemaHeaderField | undefined;
     yMargin: number;
     numGroupColumns: number;
@@ -61,9 +62,9 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
 
     // columnWidth returns the width of a column in absolute pixels
     @computed get columnWidth() {
-        if (!this.props.columnHeaders || !this.props.headingObject || this.props.columnHeaders.length === 1) return '100%';
-        const i = this.props.columnHeaders.indexOf(this.props.headingObject);
-        return this.props.columnHeaders[i].width * 100 + '%';
+        if (!this.props.colHeaderData || !this.props.headingObject || this.props.colHeaderData.length === 1) return '100%';
+        const i = this.props.colHeaderData.indexOf(this.props.headingObject);
+        return this.props.colHeaderData[i].width * 100 + '%';
     }
 
     private dropDisposer?: DragManager.DragDropDisposer;
@@ -105,7 +106,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     headingChanged = (value: string, shiftDown?: boolean) => {
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
+            if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
                 return false;
             }
             this.props.docList.forEach(d => (d[this.props.pivotField] = castedValue));
@@ -127,7 +128,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
         const key = this.props.pivotField;
-        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true });
+        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
         const colValue = this.getValue(this.props.heading);
         newDoc[key] = colValue;
         FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -141,11 +142,11 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     @undoBatch
     @action
     deleteColumn = () => {
-        const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
+        const colHdrData = Array.from(Cast(this.props.Document[this.props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         if (this.props.headingObject) {
             this.props.docList.forEach(d => (d[this.props.pivotField] = undefined));
-            columnHeaders.splice(columnHeaders.indexOf(this.props.headingObject), 1);
-            this.props.resizeColumns(columnHeaders);
+            colHdrData.splice(colHdrData.indexOf(this.props.headingObject), 1);
+            this.props.resizeColumns(colHdrData);
         }
     };
 
@@ -211,7 +212,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true });
+            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
             if (created) {
                 if (this.props.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.Document);
@@ -243,7 +244,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                     style={{ background: evContents !== `No Value` ? this._color : 'inherit' }}>
                     <EditableView GetValue={() => evContents} isEditingCallback={isEditing => isEditing && this.props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
                 </div>
-                {(this.props.columnHeaders?.length ?? 0) > 1 && (
+                {(this.props.colHeaderData?.length ?? 0) > 1 && (
                     <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
                         <FontAwesomeIcon icon="trash" size="lg" />
                     </button>
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 5b96a8682..ea0fbbc54 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -82,8 +82,8 @@ export class CollectionPileView extends CollectionSubView() {
             this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
             this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
             DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
-            this.layoutDoc._panX = 0;
-            this.layoutDoc._panY = -10;
+            this.layoutDoc._freeform_panX = 0;
+            this.layoutDoc._freeform_panY = -10;
             this.props.Document._pileLayoutEngine = computePassLayout.name;
         } else {
             const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500);
@@ -91,7 +91,7 @@ export class CollectionPileView extends CollectionSubView() {
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2;
             this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym]();
             this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym]();
-            this.layoutDoc._panX = this.layoutDoc._panY = 0;
+            this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
             this.layoutDoc._width = this.layoutDoc._height = defaultSize;
             this.props.Document._pileLayoutEngine = computeStarburstLayout.name;
         }
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 22a575989..6b4c8a3e9 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -24,7 +24,7 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { AudioWaveform } from '../AudioWaveform';
 import { CollectionSubView } from '../collections/CollectionSubView';
 import { LightboxView } from '../LightboxView';
-import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView';
 import { LabelBox } from '../nodes/LabelBox';
 import { VideoBox } from '../nodes/VideoBox';
 import './CollectionStackedTimeline.scss';
@@ -41,7 +41,9 @@ export type CollectionStackedTimelineProps = {
     mediaPath: string;
     dictationKey: string;
     rawDuration: number;
+    dataFieldKey: string;
     fieldKey: string;
+    thumbnails?: () => string[];
 };
 
 // trimming state: shows full clip, current trim bounds, or not trimming
@@ -80,6 +82,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     get minTrimLength() {
         return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5);
     }
+    @computed get thumbnails() {
+        return this.props.thumbnails?.();
+    }
 
     @computed get trimStart() {
         return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart;
@@ -102,7 +107,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     }
 
     @computed get currentTime() {
-        return NumCast(this.layoutDoc._currentTimecode);
+        return NumCast(this.layoutDoc._layout_currentTimecode);
     }
 
     @computed get zoomFactor() {
@@ -219,12 +224,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     };
 
     getLinkData(l: Doc) {
-        let la1 = l.anchor1 as Doc;
-        let la2 = l.anchor2 as Doc;
+        let la1 = l.link_anchor_1 as Doc;
+        let la2 = l.link_anchor_2 as Doc;
         const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag]));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
-            la1 = l.anchor2 as Doc;
-            la2 = l.anchor1 as Doc;
+            la1 = l.link_anchor_2 as Doc;
+            la2 = l.link_anchor_1 as Doc;
         }
         return { la1, la2, linkTime };
     }
@@ -292,10 +297,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         const clientX = e.clientX;
         if (rect) {
             this._hoverTime = this.toTimeline(clientX - rect.x, rect.width);
-            if (this.dataDoc.thumbnails) {
+            if (this.thumbnails) {
                 const nearest = Math.floor((this._hoverTime / this.props.rawDuration) * VideoBox.numThumbnails);
-                const thumbnails = StrListCast(this.dataDoc.thumbnails);
-                const imgField = thumbnails?.length > 0 ? new ImageField(thumbnails[nearest]) : undefined;
+                const imgField = this.thumbnails.length > 0 ? new ImageField(this.thumbnails[nearest]) : undefined;
                 this._thumbnail = imgField?.url?.href ? imgField.url.href.replace('.png', '_m.png') : undefined;
             }
         }
@@ -401,11 +405,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
                 _minFontSize: 12,
                 _maxFontSize: 24,
-                _singleLine: true,
                 _stayInCollection: true,
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
-                useLinkSmallAnchor: true,
-                hideLinkButton: true,
+                layout_hideLinkButton: true,
                 onClick: FollowLinkScript(),
                 annotationOn: rootDoc,
                 _timelineLabel: true,
@@ -434,7 +436,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 this.scrollToTime(seekTimeInSeconds);
             }
         } else {
-            if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) {
+            if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) && endTime > NumCast(this.layoutDoc._layout_currentTimecode)) {
                 if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) {
                     this.props.Pause();
                 } else {
@@ -455,7 +457,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         }
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
         const endTime = this.anchorEnd(anchorDoc);
-        if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
+        if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._layout_currentTimecode) - 1e-4) {
             if (this.props.playing()) this.props.Pause();
             else if (this.layoutDoc.autoPlayAnchors) this.props.Play();
             else if (!this.layoutDoc.autoPlayAnchors) {
@@ -596,6 +598,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         {!this.props.PanelHeight() ? null : (
                             <AudioWaveform
                                 rawDuration={this.props.rawDuration}
+                                fieldKey={this.props.dataFieldKey}
                                 duration={this.clipDuration}
                                 mediaPath={this.props.mediaPath}
                                 layoutDoc={this.layoutDoc}
@@ -661,6 +664,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
 interface StackedTimelineAnchorProps {
     mark: Doc;
+    whenChildContentsActiveChanged: (isActive: boolean) => void;
+    addDocTab: (doc: Doc, where: OpenWhere) => boolean;
     rangeClickScript: () => ScriptField;
     rangePlayScript: () => ScriptField;
     left: number;
@@ -709,7 +714,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             () => this.props.currentTimecode(),
             time => {
                 const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null);
-                const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc);
+                const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc);
                 if (
                     !LightboxView.LightboxDoc &&
                     // bcz: when should links be followed?  we don't want to move away from the video to follow a link but we can open it in a sidebar/etc.  But we don't know that upfront.
@@ -808,11 +813,9 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     isDocumentActive={this.props.isDocumentActive}
                     PanelWidth={width}
                     PanelHeight={height}
-                    fitWidth={returnTrue}
+                    layout_fitWidth={returnTrue}
                     ScreenToLocalTransform={screenXf}
-                    addDocTab={returnFalse}
                     pinToPres={emptyFunction}
-                    whenChildContentsActiveChanged={emptyFunction}
                     focus={focusFunc}
                     isContentActive={returnFalse}
                     searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index eedf639aa..404ca5d65 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -49,7 +49,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     // Not sure what a pivot field is. Seems like we cause reaction in MobX get rid of it once we exit this view
     _pivotFieldDisposer?: IReactionDisposer;
     // Seems like we cause reaction in MobX get rid of our height once we exit this view
-    _autoHeightDisposer?: IReactionDisposer;
+    _layout_autoHeightDisposer?: IReactionDisposer;
     // keeping track of documents. Updated on internal and external drops. What's the difference?
     _docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
     // Doesn't look like this field is being used anywhere. Obsolete?
@@ -65,8 +65,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
     }
     // it looks like this gets the column headers that Mehek was showing just now
-    @computed get columnHeaders() {
-        return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null);
+    @computed get colHeaderData() {
+        return Cast(this.layoutDoc['_' + this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
     }
     // Still not sure what a pivot is, but it appears that we can actually filter docs somehow?
     @computed get pivotField() {
@@ -116,10 +116,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     constructor(props: any) {
         super(props);
 
-        if (this.columnHeaders === undefined) {
+        if (this.colHeaderData === undefined) {
             // TODO: what is a layout doc? Is it literally how this document is supposed to be layed out?
             // here we're making an empty list of column headers (again, what Mehek showed us)
-            this.layoutDoc._columnHeaders = new List<SchemaHeaderField>();
+            this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
         }
     }
 
@@ -154,14 +154,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     //TODO: this seems important
     get Sections() {
         // appears that pivot field IS actually for sorting
-        if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
+        if (!this.pivotField || this.colHeaderData instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
 
-        if (this.columnHeaders === undefined) {
-            setTimeout(() => (this.layoutDoc._columnHeaders = new List<SchemaHeaderField>()), 0);
+        if (this.colHeaderData === undefined) {
+            setTimeout(() => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>()), 0);
             return new Map<SchemaHeaderField, Doc[]>();
         }
-        const columnHeaders = Array.from(this.columnHeaders);
-        const fields = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+        const colHeaderData = Array.from(this.colHeaderData);
+        const fields = new Map<SchemaHeaderField, Doc[]>(colHeaderData.map(sh => [sh, []] as [SchemaHeaderField, []]));
         let changed = false;
         this.filteredChildren.map(d => {
             const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object;
@@ -170,13 +170,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
 
             // look for if header exists already
-            const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
+            const existingHeader = colHeaderData.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
             if (existingHeader) {
                 fields.get(existingHeader)!.push(d);
             } else {
                 const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
-                columnHeaders.push(newSchemaHeader);
+                colHeaderData.push(newSchemaHeader);
                 changed = true;
             }
         });
@@ -187,13 +187,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 .filter(key => !fields.get(key)!.length)
                 .map(header => {
                     fields.delete(header);
-                    columnHeaders.splice(columnHeaders.indexOf(header), 1);
+                    colHeaderData.splice(colHeaderData.indexOf(header), 1);
                     changed = true;
                 });
         }
         changed &&
             setTimeout(
-                action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)),
+                action(() => this.colHeaderData?.splice(0, this.colHeaderData.length, ...colHeaderData)),
                 0
             );
         return fields;
@@ -206,12 +206,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         // reset section headers when a new filter is inputted
         this._pivotFieldDisposer = reaction(
             () => this.pivotField,
-            () => (this.layoutDoc._columnHeaders = new List())
+            () => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List())
         );
-        this._autoHeightDisposer = reaction(
-            () => this.layoutDoc._autoHeight,
-            autoHeight =>
-                autoHeight &&
+        this._layout_autoHeightDisposer = reaction(
+            () => this.layoutDoc._layout_autoHeight,
+            layout_autoHeight =>
+                layout_autoHeight &&
                 this.props.setHeight?.(
                     Math.min(
                         NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
@@ -224,7 +224,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     componentWillUnmount() {
         super.componentWillUnmount();
         this._pivotFieldDisposer?.();
-        this._autoHeightDisposer?.();
+        this._layout_autoHeightDisposer?.();
     }
 
     isAnyChildContentActive = () => this.props.isAnyChildContentActive();
@@ -282,12 +282,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         if (docView && ['Enter'].includes(e.key) && e.ctrlKey) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== 'Tab';
-            const layoutKey = StrCast(docView.LayoutFieldKey);
+            const layout_fieldKey = StrCast(docView.LayoutFieldKey);
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
-                newDoc[layoutKey] = docView.rootDoc[layoutKey];
+            if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
+                newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -327,19 +327,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 pointerEvents={this.props.DocumentView?.().props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
                 styleProvider={this.styleProvider}
                 docViewPath={this.props.docViewPath}
-                fitWidth={this.props.childFitWidth}
+                layout_fitWidth={this.props.childLayoutFitWidth}
                 isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
                 onBrowseClick={this.props.onBrowseClick}
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this.props.childLayoutTemplate}
                 LayoutTemplateString={this.props.childLayoutString}
-                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
-                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
+                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
                 dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
-                dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't autoHeight resize if dontRegisterView is set, but they need to.
+                dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
                 rootSelected={this.rootSelected}
-                showTitle={this.props.childShowTitle}
+                layout_showTitle={this.props.childlayout_showTitle}
                 dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
@@ -376,7 +376,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         if (!d) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const maxWidth = this.columnWidth / this.numGroupColumns;
-        if (!this.layoutDoc._columnsFill && !(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d))) {
+        if (!this.layoutDoc._columnsFill && !(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d))) {
             return Math.min(d[WidthSym](), maxWidth);
         }
         return maxWidth;
@@ -386,15 +386,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
-        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
-        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[WidthSym]() : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
             const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
             return Math.min(maxHeight, (docWid * nh) / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
@@ -460,10 +460,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     docs.splice(insertInd - offset, 0, ...newDocs);
                 }
             }
-        } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
-            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
+        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
         } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
@@ -526,7 +526,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         this.refList.push(ref);
                         this.observer = new _global.ResizeObserver(
                             action((entries: any) => {
-                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                                     const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
                                     if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
                                         this.props.setHeight?.(height);
@@ -539,7 +539,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 }}
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
-                columnHeaders={this.columnHeaders}
+                colHeaderData={this.colHeaderData}
                 Document={this.props.Document}
                 DataDoc={this.props.DataDoc}
                 renderChildren={this.children}
@@ -581,7 +581,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         this.refList.push(ref);
                         this.observer = new _global.ResizeObserver(
                             action((entries: any) => {
-                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                                     const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
                                     this.props.setHeight?.(this.headerMargin + height);
                                 }
@@ -608,9 +608,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     /// add a new group category (column) to the active set of note categories.  (e.g., if the pivot field is 'transportation', groups might be 'car', 'plane', 'bike', etc)
     @action
     addGroup = (value: string) => {
-        if (value && this.columnHeaders) {
+        if (value && this.colHeaderData) {
             const schemaHdrField = new SchemaHeaderField(value);
-            this.columnHeaders.push(schemaHdrField);
+            this.colHeaderData.push(schemaHdrField);
             return true;
         }
         return false;
@@ -630,7 +630,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const options = cm.findByDescription('Options...');
             const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
             optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
-            optionItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+            optionItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
             optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' });
             !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
         }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index d62c4dc62..243550c0b 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -34,7 +34,7 @@ interface CSVFieldColumnProps {
     heading: string;
     pivotField: string;
     chromeHidden?: boolean;
-    columnHeaders: SchemaHeaderField[] | undefined;
+    colHeaderData: SchemaHeaderField[] | undefined;
     headingObject: SchemaHeaderField | undefined;
     yMargin: number;
     columnWidth: number;
@@ -93,7 +93,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @undoBatch
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
         const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
-        drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
+        this.props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
     });
     getValue = (value: string): any => {
         const parsed = parseInt(value);
@@ -107,7 +107,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     headingChanged = (value: string, shiftDown?: boolean) => {
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
+            if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
                 return false;
             }
             this.props.docList.forEach(d => (d[this.props.pivotField] = castedValue));
@@ -134,7 +134,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
         const key = this.props.pivotField;
-        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true });
+        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
         newDoc[key] = this.getValue(this.props.heading);
         const maxHeading = this.props.docList.reduce((maxHeading, doc) => (NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading), 0);
         const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
@@ -147,9 +147,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @action
     deleteColumn = () => {
         this.props.docList.forEach(d => (d[this.props.pivotField] = undefined));
-        if (this.props.columnHeaders && this.props.headingObject) {
-            const index = this.props.columnHeaders.indexOf(this.props.headingObject);
-            this.props.columnHeaders.splice(index, 1);
+        if (this.props.colHeaderData && this.props.headingObject) {
+            const index = this.props.colHeaderData.indexOf(this.props.headingObject);
+            this.props.colHeaderData.splice(index, 1);
         }
     };
 
@@ -163,15 +163,15 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     //TODO: I think this is where I'm supposed to edit stuff
     startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
-        // is MakeAlias a way to make a copy of a doc without rendering it?
-        const alias = Doc.MakeAlias(this.props.Document);
-        alias._width = this.props.columnWidth / (this.props.columnHeaders?.length || 1);
-        alias._pivotField = undefined;
+        // is MakeEmbedding a way to make a copy of a doc without rendering it?
+        const embedding = Doc.MakeEmbedding(this.props.Document);
+        embedding._width = this.props.columnWidth / (this.props.colHeaderData?.length || 1);
+        embedding._pivotField = undefined;
         let value = this.getValue(this._heading);
         value = typeof value === 'string' ? `"${value}"` : value;
-        alias.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name });
-        if (alias.viewSpecScript) {
-            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
+        embedding.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name });
+        if (embedding.viewSpecScript) {
+            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
             return true;
         }
         return false;
@@ -266,7 +266,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true });
+            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
             if (created) {
                 if (this.props.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.Document);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5b9453666..008de1944 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -129,7 +129,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                     notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, this.props.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];
+                    const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                     if (data !== undefined) {
                         let subDocs = DocListCast(data);
                         if (subDocs.length > 0) {
@@ -141,7 +141,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                     const fieldKey = Doc.LayoutFieldKey(t);
                                     const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name);
                                     notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, d).length));
-                                    DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
+                                    DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                                 });
                                 subDocs = newarray;
                             }
@@ -188,9 +188,9 @@ export function CollectionSubView<X>(moreProps?: X) {
 
         protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
             if (de.complete.docDragData) {
-                // if targetDropAction is, say 'alias', but we're just dragging within a collection, we want to ignore the targetAction.
+                // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
                 // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys)
-                if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.context === this.props.Document && this.childDocs.includes(d))) {
+                if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
                     de.complete.docDragData.dropAction = targetAction;
                 }
                 e.stopPropagation();
@@ -282,7 +282,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                             addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
                         }
                     } else if (text) {
-                        addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: StrCast(Doc.UserDoc().showTitle), _width: 100, _height: 25 }));
+                        addDocument(Docs.Create.TextDocument(text, { ...options, _layout_showTitle: StrCast(Doc.UserDoc().layout_showTitle), _width: 100, _height: 25 }));
                     }
                     return;
                 }
@@ -375,7 +375,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                         _width: 400,
                         _height: 512,
                         _nativeWidth: 850,
-                        useCors: true,
+                        data_useCors: true,
                     })
                 );
                 return;
@@ -417,7 +417,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                         proto._height = 20;
                                         return proto;
                                     }),
-                                    { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _singleLine: true }
+                                    { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _createDocOnCR: true }
                                 )
                             );
                         });
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 3cdb460a3..6135154c3 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -35,7 +35,7 @@ export class CollectionTimeView extends CollectionSubView() {
     async componentDidMount() {
         this.props.setContentView?.(this);
         //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), "");
-        ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; useRightSplit(alias, shiftKey); ";
+        ///const childText = "const embedding = getEmbedding(self); switchView(embedding, detailView); embedding.dropAction='embed'; useRightSplit(embedding, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
@@ -55,10 +55,10 @@ export class CollectionTimeView extends CollectionSubView() {
 
         if (addAsAnnotation) {
             // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
-                Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+            if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
+                Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
             } else {
-                this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+                this.dataDoc[this.props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
             }
         }
         return anchor;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f774781ab..095e34c39 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -104,7 +104,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     componentDidMount() {
         //this.props.setContentView?.(this);
         this._disposers.autoheight = reaction(
-            () => this.rootDoc.autoHeight,
+            () => this.rootDoc.layout_autoHeight,
             auto => auto && this.computeHeight(),
             { fireImmediately: true }
         );
@@ -114,25 +114,25 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         if (!this._isDisposing) {
             const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
             const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()) + 6;
-            this.layoutDoc._autoHeightMargins = bodyHeight;
+            this.layoutDoc._layout_autoHeightMargins = bodyHeight;
             !this.props.dontRegisterView && this.props.setHeight?.(bodyHeight + titleHeight);
         }
     };
     unobserveHeight = (ref: any) => {
         this.refList.delete(ref);
-        this.rootDoc.autoHeight && this.computeHeight();
+        this.rootDoc.layout_autoHeight && this.computeHeight();
     };
     observeHeight = (ref: any) => {
         if (ref) {
             this.refList.add(ref);
             this.observer = new _global.ResizeObserver(
                 action((entries: any) => {
-                    if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+                    if (this.rootDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
                         this.computeHeight();
                     }
                 })
             );
-            this.rootDoc.autoHeight && this.computeHeight();
+            this.rootDoc.layout_autoHeight && this.computeHeight();
             this.observer.observe(ref);
         }
     };
@@ -144,7 +144,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
+            const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d));
             dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction;
         }
     };
@@ -176,7 +176,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const doAddDoc = (doc: Doc | Doc[]) =>
             (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => {
                 const res = flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before);
-                res && (doc.context = this.props.Document);
+                res && (doc.embedContainer = this.props.Document);
                 return res;
             }, true);
         if (this.doc.resolvedDataDoc instanceof Promise) return false;
@@ -234,7 +234,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 isContentActive={this.isContentActive}
                 isDocumentActive={this.isContentActive}
                 rootSelected={returnTrue}
-                forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
+                forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not layout_autoHeight
                 PanelWidth={this.documentTitleWidth}
                 PanelHeight={this.documentTitleHeight}
                 NativeDimScaling={returnOne}
@@ -369,15 +369,15 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     marginTop = () => NumCast(this.doc._yMargin);
     marginBot = () => NumCast(this.doc._yMargin);
     documentTitleWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.panelWidth());
-    documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins);
+    documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.layout_autoHeightMargins);
     truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
     onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
     panelWidth = () => Math.max(0, this.props.PanelWidth() - 2 * this.marginX() * (this.props.NativeDimScaling?.() || 1));
 
-    addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
-    remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
+    addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}_annotations`) || false;
+    remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}_annotations`) || false;
     moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
-        this.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false;
+        this.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}_annotations`) || false;
 
     @observable _headerHeight = 0;
     @computed get content() {
@@ -430,7 +430,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     render() {
         TraceMobx();
 
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1) || 1;
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
         return (
             <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
                 {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
@@ -443,7 +443,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                         isAnnotationOverlay={true}
                         isAnnotationOverlayScrollable={true}
                         childDocumentsActive={this.props.isDocumentActive}
-                        fieldKey={this.props.fieldKey + '-annotations'}
+                        fieldKey={this.props.fieldKey + '_annotations'}
                         dropAction={'move'}
                         select={emptyFunction}
                         addDocument={this.addAnnotationDocument}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b76033a0c..dd7bf1740 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -47,8 +47,8 @@ interface CollectionViewProps_ extends FieldViewProps {
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childContentsActive?: () => boolean | undefined;
-    childFitWidth?: (child: Doc) => boolean;
-    childShowTitle?: () => string;
+    childLayoutFitWidth?: (child: Doc) => boolean;
+    childlayout_showTitle?: () => string;
     childOpacity?: () => number;
     childContextMenuItems?: () => { script: ScriptField; label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
@@ -134,8 +134,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 { description: 'Freeform',    event: () => func(CollectionViewType.Freeform), icon: 'signature' },
                 { description: 'Schema',      event: () => func(CollectionViewType.Schema), icon: 'th-list' },
                 { description: 'Tree',        event: () => func(CollectionViewType.Tree), icon: 'tree' },
-                { description: 'Stacking',    event: () => (func(CollectionViewType.Stacking)._autoHeight = true), icon: 'ellipsis-v' },
-                { description: 'Notetaking',  event: () => (func(CollectionViewType.NoteTaking)._autoHeight = true), icon: 'ellipsis-v' },
+                { description: 'Stacking',    event: () => (func(CollectionViewType.Stacking)._layout_autoHeight = true), icon: 'ellipsis-v' },
+                { description: 'Notetaking',  event: () => (func(CollectionViewType.NoteTaking)._layout_autoHeight = true), icon: 'ellipsis-v' },
                 { description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' },
                 { description: 'Multirow',    event: () => func(CollectionViewType.Multirow), icon: 'columns' },
                 { description: 'Masonry',     event: () => func(CollectionViewType.Masonry), icon: 'columns' },
@@ -159,7 +159,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             !Doc.noviceMode &&
                 this.setupViewTypes('UI Controls...', vtype => {
-                    const newRendition = Doc.MakeAlias(this.rootDoc);
+                    const newRendition = Doc.MakeEmbedding(this.rootDoc);
                     newRendition._viewType = vtype;
                     this.props.addDocTab(newRendition, OpenWhere.addRight);
                     return newRendition;
@@ -190,9 +190,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                         description: `Edit ${func.name} script`,
                         icon: 'edit',
                         event: (obj: any) => {
-                            const alias = Doc.MakeAlias(this.rootDoc);
-                            DocUtils.makeCustomViewClicked(alias, undefined, func.key);
-                            this.props.addDocTab(alias, OpenWhere.addRight);
+                            const embedding = Doc.MakeEmbedding(this.rootDoc);
+                            DocUtils.makeCustomViewClicked(embedding, undefined, func.key);
+                            this.props.addDocTab(embedding, OpenWhere.addRight);
                         },
                     })
                 );
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 45604c1bf..69963736b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -252,11 +252,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const pinDoc = Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
             pinDoc.presentationTargetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
-            pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
+            pinDoc.data = new List<Doc>(); // the children of the embedding's layout are the presentation slide children.  the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
             pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
             pinDoc.presDuration = pinDoc.presDuration ?? 1000;
             pinDoc.groupWithUp = false;
-            pinDoc.context = curPres;
+            pinDoc.embedContainer = curPres;
             // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
             pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
             pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
@@ -264,7 +264,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc.treeViewFieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
             pinDoc.treeViewExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
             pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
-            const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+            const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null);
 
             if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
             if (!pinProps?.audioRange && duration !== undefined) {
@@ -283,7 +283,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.title = doc.title + ' (move)';
                 pinDoc.presMovement = PresMovement.Pan;
             }
-            if (pinDoc.isInkMask) {
+            if (pinDoc.stroke_isInkMask) {
                 pinDoc.presHideAfter = true;
                 pinDoc.presHideBefore = true;
                 pinDoc.presMovement = PresMovement.None;
@@ -357,7 +357,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         switch (whereFields[0]) {
             case undefined:
             case OpenWhere.lightbox:   if (this.layoutDoc?._isLightbox) {
-                                            const lightboxView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
+                                            const lightboxView = !doc.annotationOn && DocCast(doc.embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.embedContainer)) : undefined;
                                             const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)];
                                             if (lightboxView && (!data || data instanceof List)) {
                                                 lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List<Doc>([doc]);
@@ -409,7 +409,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     miniMapColor = () => this.tabColor;
     tabView = () => this._view;
     disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform;
-    hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
+    hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.layout_hideMinimap);
 
     @computed get docView() {
         return !this._activated || !this._document ? null : (
@@ -449,19 +449,19 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     pinToPres={TabDocView.PinDoc}
                 />
                 <TabMinimapView key="minimap" hideMinimap={this.hideMinimap} addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
-                <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
+                <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
                     <div
                         className="miniMap-hidden"
                         style={{
                             display: this.disableMinimap() || this._document._viewType !== 'freeform' ? 'none' : undefined,
-                            color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE,
-                            backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
-                            boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
+                            color: this._document.layout_hideMinimap ? Colors.BLACK : Colors.WHITE,
+                            backgroundColor: this._document.layout_hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
+                            boxShadow: this._document.layout_hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
                         }}
                         onPointerDown={e => e.stopPropagation()}
                         onClick={action(e => {
                             e.stopPropagation();
-                            this._document!.hideMinimap = !this._document!.hideMinimap;
+                            this._document!.layout_hideMinimap = !this._document!.layout_hideMinimap;
                         })}>
                         <FontAwesomeIcon icon={'globe-asia'} size="lg" />
                     </div>
@@ -559,8 +559,8 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                 this,
                 e,
                 action((e: PointerEvent, down: number[], delta: number[]) => {
-                    doc._panX = clamp(NumCast(doc._panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
-                    doc._panY = clamp(NumCast(doc._panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
+                    doc._freeform_panX = clamp(NumCast(doc._freeform_panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
+                    doc._freeform_panY = clamp(NumCast(doc._freeform_panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
                     return false;
                 }),
                 emptyFunction,
@@ -569,10 +569,10 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
     };
     render() {
         if (!this.renderBounds) return null;
-        const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100;
-        const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100;
-        const miniLeft = 50 + ((NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2;
-        const miniTop = 50 + ((NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
+        const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100;
+        const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100;
+        const miniLeft = 50 + ((NumCast(this.props.document._freeform_) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2;
+        const miniTop = 50 + ((NumCast(this.props.document._freeform_panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
         const miniSize = this.returnMiniSize();
         return this.props.hideMinimap() ? null : (
             <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 4adf86683..92b70cb5a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -102,7 +102,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _treedropDisposer?: DragManager.DragDropDisposer;
 
     get treeViewOpenIsTransient() {
-        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc);
+        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDocDataProto(this.doc);
     }
     set treeViewOpen(c: boolean) {
         if (this.treeViewOpenIsTransient) this._transientOpenState = c;
@@ -125,7 +125,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             : this.props.treeView.fileSysMode
             ? this.doc.isFolder
                 ? this.fieldKey
-                : 'aliases' // for displaying
+                : 'embeddings' // for displaying
             : this.props.treeView.outlineMode || this.childDocs
             ? this.fieldKey
             : Doc.noviceMode
@@ -160,11 +160,11 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get childLinks() {
         return this.childDocList('links');
     }
-    @computed get childAliases() {
-        return this.childDocList('aliases');
+    @computed get childEmbeddings() {
+        return this.childDocList('proto_embeddings');
     }
     @computed get childAnnos() {
-        return this.childDocList(this.fieldKey + '-annotations');
+        return this.childDocList(this.fieldKey + '_annotations');
     }
     @computed get selected() {
         return SelectionManager.IsSelected(this._docRef);
@@ -219,11 +219,13 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
-            // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            const bestAlias =
-                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
-            const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail);
-            this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), OpenWhere.lightbox);
+            // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns,  (2) has no context field ... otherwise make a new embedding
+            const bestEmbedding =
+                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDocDataProto(docView.props.Document)
+                    ? docView.props.Document
+                    : DocListCast(this.props.document.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail);
+            const nextBestEmbedding = DocListCast(this.props.document.proto_embeddings).find(doc => doc.author === Doc.CurrentUserEmail);
+            this.props.addDocTab(bestEmbedding ?? nextBestEmbedding ?? Doc.MakeEmbedding(this.props.document), OpenWhere.lightbox);
         }
     };
 
@@ -311,16 +313,16 @@ export class TreeView extends React.Component<TreeViewProps> {
             treeViewExpandedViewLock: true,
             treeViewExpandedView: 'data',
             _viewType: CollectionViewType.Tree,
-            hideLinkButton: true,
-            _showSidebar: true,
-            _fitWidth: true,
+            layout_hideLinkButton: true,
+            _layout_showSidebar: true,
+            _layout_fitWidth: true,
             treeViewType: TreeViewType.outline,
             x: 0,
             y: 0,
             _xMargin: 0,
             _yMargin: 0,
-            _autoHeight: true,
-            _singleLine: true,
+            _layout_autoHeight: true,
+            _createDocOnCR: true,
             _width: 1000,
             _height: 10,
         });
@@ -359,7 +361,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (de.complete.linkDragData) {
             const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
             const destDoc = this.doc;
-            DocUtils.MakeLink(sourceDoc, destDoc, { linkRelationship: 'tree link' });
+            DocUtils.MakeLink(sourceDoc, destDoc, { link_relationship: 'tree link' });
             e.stopPropagation();
         }
         const docDragData = de.complete.docDragData;
@@ -378,7 +380,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const innerAdd = (doc: Doc) => {
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
-                dataIsComputed && (doc.context = this.doc.context);
+                dataIsComputed && (doc.embedContainer = this.doc.embedContainer);
                 return added;
             };
             return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -413,7 +415,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             (() => {
                 const aspect = Doc.NativeAspect(layoutDoc);
                 if (aspect) return this.embeddedPanelWidth() / (aspect || 1);
-                return layoutDoc._fitWidth
+                return layoutDoc._layout_fitWidth
                     ? !Doc.NativeHeight(layoutDoc)
                         ? NumCast(layoutDoc._height)
                         : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.treeViewParent._height)))
@@ -441,7 +443,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     const innerAdd = (doc: Doc) => {
                         const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                         const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                        dataIsComputed && (doc.context = this.doc.context);
+                        dataIsComputed && (doc.embedContainer = this.doc.embedContainer);
                         return added;
                     };
                     return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -527,7 +529,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const expandKey = this.treeViewExpandedView;
         const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } }) ?? {};
-        if (['links', 'annotations', 'aliases', this.fieldKey].includes(expandKey)) {
+        if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) {
             const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
             const sortKeys = Object.keys(sortings);
             const curSortIndex = Math.max(
@@ -549,12 +551,12 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                !dataIsComputed && added && (doc.context = this.doc.context);
+                !dataIsComputed && added && (doc.embedContainer = this.doc.embedContainer);
 
                 return added;
             };
             const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-            const docs = expandKey === 'aliases' ? this.childAliases : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
+            const docs = expandKey === 'embeddings' ? this.childEmbeddings : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
             let downX = 0,
                 downY = 0;
             if (docs?.length && this._renderCount < docs?.length) {
@@ -706,13 +708,13 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     @computed get validExpandViewTypes() {
-        const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
+        const annos = () => (DocListCast(this.doc[this.fieldKey + '_annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
         const links = () => (LinkManager.Links(this.doc).length && !this.props.treeView.dashboardMode ? 'links' : '');
         const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : '');
-        const aliases = () => (this.props.treeView.dashboardMode ? '' : 'aliases');
+        const embeddings = () => (this.props.treeView.dashboardMode ? '' : 'embeddings');
         const fields = () => (Doc.noviceMode ? '' : 'fields');
         const layout = Doc.noviceMode || this.doc.viewType === CollectionViewType.Docking ? [] : ['layout'];
-        return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
+        return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [embeddings(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
     expandNextviewType = () => {
@@ -759,7 +761,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
         const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' };
         const folderOp = this.childDocs?.length ? [makeFolder] : [];
-        const openAlias = { script: ScriptField.MakeFunction(`openDoc(getAlias(self), ${OpenWhere.addRight})`)!, icon: 'copy', label: 'Open Alias' };
+        const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(self), ${OpenWhere.addRight})`)!, icon: 'copy', label: 'Open Embedding' };
         const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
         return [
             ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
@@ -768,10 +770,10 @@ export class TreeView extends React.Component<TreeViewProps> {
                 : Doc.IsSystem(this.doc)
                 ? []
                 : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
-                ? [openAlias, makeFolder]
+                ? [openEmbedding, makeFolder]
                 : this.doc.viewType === CollectionViewType.Docking
                 ? []
-                : [deleteItem, openAlias, focusDoc]),
+                : [deleteItem, openEmbedding, focusDoc]),
         ];
     };
     childContextMenuItems = () => {
@@ -896,7 +898,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     }
                 })}
                 Document={this.doc}
-                fitWidth={returnTrue}
+                layout_fitWidth={returnTrue}
                 DataDoc={undefined}
                 scriptContext={this}
                 hideDecorationTitle={this.props.treeView.outlineMode}
@@ -985,7 +987,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     ref={action((r: DocumentView | null) => (this._dref = r))}
                     Document={this.doc}
                     DataDoc={undefined}
-                    fitWidth={this.fitWidthFilter}
+                    layout_fitWidth={this.fitWidthFilter}
                     PanelWidth={this.embeddedPanelWidth}
                     PanelHeight={this.embeddedPanelHeight}
                     LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
@@ -1179,7 +1181,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
                         Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
                         newParent.treeViewOpen = true;
-                        child.context = treeView.Document;
+                        child.embedContainer = treeView.Document;
                     }
                 };
                 const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 0dfd119d7..68ba3d368 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -34,11 +34,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         this._anchorDisposer = reaction(
             () => [
                 this.props.A.props.ScreenToLocalTransform(),
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
                 this.props.B.props.ScreenToLocalTransform(),
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
             ],
             action(() => {
                 this._start = Date.now();
@@ -58,7 +58,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             0
         ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
         setTimeout(
-            action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)),
+            action(() => (!LinkDocs.length || !linkDoc.layout_linkDisplay) && (this._opacity = 0.05)),
             750
         ); // this will unhighlight the link line.
         const a = A.ContentDiv.getBoundingClientRect();
@@ -72,36 +72,36 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         //   if there's an element in the DOM with a classname containing a link anchor's id,
         //   then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
         //   otherwise, we just use the computed nearest point on the document boundary to the target Document
-        const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement();
-        const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor2 as Doc)[Id])).lastElement();
+        const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.link_anchor_1 as Doc)[Id])).lastElement();
+        const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.link_anchor_2 as Doc)[Id])).lastElement();
         if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
         if (!targetAhyperlink) {
-            if (linkDoc.linkAutoMove) {
-                linkDoc.anchor1_x = ((apt.point.x - aleft) / awidth) * 100;
-                linkDoc.anchor1_y = ((apt.point.y - atop) / aheight) * 100;
+            if (linkDoc.layout_autoMoveAnchors) {
+                linkDoc.link_anchor_1_x = ((apt.point.x - aleft) / awidth) * 100;
+                linkDoc.link_anchor_1_y = ((apt.point.y - atop) / aheight) * 100;
             }
         } else {
             const m = targetAhyperlink.getBoundingClientRect();
             const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
             const mpx = mp[0] / A.props.PanelWidth();
             const mpy = mp[1] / A.props.PanelHeight();
-            if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
-            if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
+            if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_1_x = mpx * 100;
+            if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_1_y = mpy * 100;
             if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
         if (!targetBhyperlink) {
-            if (linkDoc.linkAutoMove) {
-                linkDoc.anchor2_x = ((bpt.point.x - bleft) / bwidth) * 100;
-                linkDoc.anchor2_y = ((bpt.point.y - btop) / bheight) * 100;
+            if (linkDoc.layout_autoMoveAnchors) {
+                linkDoc.link_anchor_2_x = ((bpt.point.x - bleft) / bwidth) * 100;
+                linkDoc.link_anchor_2_y = ((bpt.point.y - btop) / bheight) * 100;
             }
         } else {
             const m = targetBhyperlink.getBoundingClientRect();
             const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
             const mpx = mp[0] / B.props.PanelWidth();
             const mpy = mp[1] / B.props.PanelHeight();
-            if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
-            if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
+            if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_2_x = mpx * 100;
+            if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_2_y = mpy * 100;
             if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
@@ -112,8 +112,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             this,
             e,
             (e, down, delta) => {
-                this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
-                this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
+                this.props.LinkDocs[0].link_relationship_OffsetX = NumCast(this.props.LinkDocs[0].link_relationship_OffsetX) + delta[0];
+                this.props.LinkDocs[0].link_relationship_OffsetY = NumCast(this.props.LinkDocs[0].link_relationship_OffsetY) + delta[1];
                 return false;
             },
             emptyFunction,
@@ -205,8 +205,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const bclipped = bleft !== b.left || btop !== b.top;
         if (aclipped && bclipped) return undefined;
         const clipped = aclipped || bclipped;
-        const pt1inside = NumCast(LinkDocs[0].anchor1_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor1_y) % 100 !== 0;
-        const pt2inside = NumCast(LinkDocs[0].anchor2_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor2_y) % 100 !== 0;
+        const pt1inside = NumCast(LinkDocs[0].link_anchor_1_x) % 100 !== 0 && NumCast(LinkDocs[0].link_anchor_1_y) % 100 !== 0;
+        const pt2inside = NumCast(LinkDocs[0].link_anchor_2_x) % 100 !== 0 && NumCast(LinkDocs[0].link_anchor_2_y) % 100 !== 0;
         const pt1 = [aleft + a.width / 2, atop + a.height / 2];
         const pt2 = [bleft + b.width / 2, btop + b.width / 2];
         const pt2vec = pt2inside ? [-0.7071, 0.7071] : [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]];
@@ -223,8 +223,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const aActive = A.isSelected() || Doc.IsBrushed(A.rootDoc);
         const bActive = B.isSelected() || Doc.IsBrushed(B.rootDoc);
 
-        const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].linkOffsetX);
-        const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].linkOffsetY);
+        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);
         return {
             a,
             b,
@@ -244,12 +244,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
 
         const link = this.props.LinkDocs[0];
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
-        const linkRelationship = Field.toString(link?.linkRelationship as any as Field); //get string representing relationship
-        const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
+        const linkRelationship = Field.toString(link?.link_relationship as any as Field); //get string representing relationship
+        const linkRelationshipList = Doc.UserDoc().link_relationshipList as List<string>;
         const linkColorList = Doc.UserDoc().linkColorList as List<string>;
-        const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List<number>;
+        const linkRelationshipSizes = Doc.UserDoc().link_relationshipSizes as List<number>;
         const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship);
-        const linkDescription = Field.toString(link.description as any as Field);
+        const linkDescription = Field.toString(link.link_description as any as Field);
 
         const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
 
@@ -261,11 +261,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         //thickness varies linearly from 3px to 12px for increasing link count
         const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
 
-        if (link.linkDisplayArrow === undefined) {
-            link.linkDisplayArrow = false;
+        if (link.layout_linkDisplayArrow === undefined) {
+            link.layout_linkDisplayArrow = false;
         }
 
-        return link.opacity === 0 || !a.width || !b.width || (!link.linkDisplay && !aActive && !bActive) ? null : (
+        return link.opacity === 0 || !a.width || !b.width || (!link.layout_linkDisplay && !aActive && !bActive) ? null : (
             <>
                 <defs>
                     <marker id={`${link[Id] + 'arrowhead'}`} markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
@@ -294,7 +294,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                     style={{ pointerEvents: 'visibleStroke', opacity: this._opacity, stroke, strokeWidth }}
                     onClick={this.onClickLine}
                     d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
-                    markerEnd={link.linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
+                    markerEnd={link.layout_linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
                 />
                 {textX === undefined || !linkDescription ? null : (
                     <text filter={`url(#${link[Id] + 'background'})`} className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 08e3174d1..4d18ebeea 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -100,13 +100,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.props.isAnnotationOverlay;
     }
     public get scaleFieldKey() {
-        return this.props.viewField ? this.props.viewField + '-viewScale' : '_viewScale';
+        return (this.props.viewField ?? '') + '_freeform_scale';
     }
     private get panXFieldKey() {
-        return this.props.viewField ? this.props.viewField + '-panX' : '_panX';
+        return (this.props.viewField ?? '') + '_freeform_panX';
     }
     private get panYFieldKey() {
-        return this.props.viewField ? this.props.viewField + '-panY' : '_panY';
+        return (this.props.viewField ?? '') + '_freeform_panY';
     }
     private get borderWidth() {
         return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
@@ -146,7 +146,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         };
     }
     @computed get fitContentsToBox() {
-        return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay;
+        return (this.props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
     }
     @computed get contentBounds() {
         const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
@@ -173,7 +173,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const dv = this.props.DocumentView?.();
         const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         // if freeform has a native aspect, then the panel height needs to be adjusted to match it
-        const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
+        const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.layout_fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
         return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedGetLocalTransform(): Transform {
@@ -239,16 +239,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     shrinkWrap = () => {
         if (this.props.DocumentView?.().nativeWidth) return;
         const vals = this.fitToContentVals;
-        this.layoutDoc._panX = vals.bounds.cx;
-        this.layoutDoc._panY = vals.bounds.cy;
-        this.layoutDoc._viewScale = vals.scale;
+        this.layoutDoc._freeform_panX = vals.bounds.cx;
+        this.layoutDoc._freeform_panY = vals.bounds.cy;
+        this.layoutDoc._freeform_scale = vals.scale;
     };
     freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined);
     reverseNativeScaling = () => (this.fitContentsToBox ? true : false);
-    // panx, pany, zoomscale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
+    // freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
     // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
-    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1));
-    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1));
+    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
+    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
     zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
     contentTransform = () =>
         this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
@@ -297,7 +297,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     isCurrent(doc: Doc) {
         const dispTime = NumCast(doc._timecodeToShow, -1);
         const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
-        const curTime = NumCast(this.Document._currentTimecode, -1);
+        const curTime = NumCast(this.Document._layout_currentTimecode, -1);
         return dispTime === -1 || curTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
     }
 
@@ -348,7 +348,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             .map(pair => pair.layout)
             .slice()
             .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
-        zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1));
+        zsorted.forEach((doc, index) => (doc.zIndex = doc.stroke_isInkMask ? 5000 : index + 1));
         const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
         const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
 
@@ -366,7 +366,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 d.x = x + NumCast(d.x) - dropPos[0];
                 d.y = y + NumCast(d.y) - dropPos[1];
             }
-            d._lastModified = new DateField();
+            d._layout_modificationDate = new DateField();
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? (nd[1] / nd[0]) * NumCast(layoutDoc._width) : 300);
@@ -418,11 +418,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
         if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
             // dragged document is a child of this collection
-            if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) {
-                // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
+            if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.embedContainer !== this.props.Document) {
+                // if the source doc view's embedContainer isn't this same freeformcollectionlinkDragData.dragDocument.embedContainer === this.props.Document
                 const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' });
                 this.props.addDocument?.(source);
-                de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { linkRelationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
+                de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
             }
             e.stopPropagation(); //  do nothing if link is dropped into any freeform view parent of dragged document
             return true;
@@ -446,7 +446,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.childLayoutPairs
             .map(pair => pair.layout)
             .reduce((cluster, cd) => {
-                const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
+                const grouping = this.props.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;
@@ -463,10 +463,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (cluster !== -1) {
             const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
             if (ptsParent) {
-                const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster);
+                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 clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!);
                 const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
-                const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'alias' : undefined);
+                const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined);
                 de.moveDocument = this.props.moveDocument;
                 de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
                 DragManager.StartDocumentDrag(
@@ -485,8 +485,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @undoBatch
     @action
-    updateClusters(_useClusters: boolean) {
-        this.props.Document._useClusters = _useClusters;
+    updateClusters(_freeform_useClusters: boolean) {
+        this.props.Document._freeform_useClusters = _freeform_useClusters;
         this._clusterSets.length = 0;
         this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
     }
@@ -494,38 +494,43 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     updateClusterDocs(docs: Doc[]) {
         const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
-        if (this.props.Document._useClusters) {
+        if (this.props.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.cluster);
-            docs.map(doc => (doc.cluster = -1));
+            const preferredInd = NumCast(docFirst.layout_cluster);
+            docs.map(doc => (doc.layout_cluster = -1));
             docs.map(doc =>
                 this._clusterSets.map((set, i) =>
                     set.map(member => {
-                        if (docFirst.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
-                            docFirst.cluster = i;
+                        if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+                            docFirst.layout_cluster = i;
                         }
                     })
                 )
             );
-            if (docFirst.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
-                docFirst.cluster = preferredInd;
+            if (
+                docFirst.layout_cluster === -1 &&
+                preferredInd !== -1 &&
+                this._clusterSets.length > preferredInd &&
+                (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)
+            ) {
+                docFirst.layout_cluster = preferredInd;
             }
             this._clusterSets.map((set, i) => {
-                if (docFirst.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
-                    docFirst.cluster = i;
+                if (docFirst.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+                    docFirst.layout_cluster = i;
                 }
             });
-            if (docFirst.cluster === -1) {
+            if (docFirst.layout_cluster === -1) {
                 docs.map(doc => {
-                    doc.cluster = this._clusterSets.length;
+                    doc.layout_cluster = this._clusterSets.length;
                     this._clusterSets.push([doc]);
                 });
             } else if (this._clusterSets.length) {
-                for (let i = this._clusterSets.length; i <= NumCast(docFirst.cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
-                docs.map(doc => this._clusterSets[(doc.cluster = NumCast(docFirst.cluster))].push(doc));
+                for (let i = this._clusterSets.length; i <= NumCast(docFirst.layout_cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
+                docs.map(doc => this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc));
             }
-            childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
+            childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child));
         }
     }
 
@@ -533,31 +538,31 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     updateCluster(doc: Doc) {
         const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
-        if (this.props.Document._useClusters) {
+        if (this.props.Document._freeform_useClusters) {
             this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
-            const preferredInd = NumCast(doc.cluster);
-            doc.cluster = -1;
+            const preferredInd = NumCast(doc.layout_cluster);
+            doc.layout_cluster = -1;
             this._clusterSets.forEach((set, i) =>
                 set.forEach(member => {
-                    if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
-                        doc.cluster = i;
+                    if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+                        doc.layout_cluster = i;
                     }
                 })
             );
-            if (doc.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
-                doc.cluster = preferredInd;
+            if (doc.layout_cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
+                doc.layout_cluster = preferredInd;
             }
             this._clusterSets.forEach((set, i) => {
-                if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
-                    doc.cluster = i;
+                if (doc.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+                    doc.layout_cluster = i;
                 }
             });
-            if (doc.cluster === -1) {
-                doc.cluster = this._clusterSets.length;
+            if (doc.layout_cluster === -1) {
+                doc.layout_cluster = this._clusterSets.length;
                 this._clusterSets.push([doc]);
             } else if (this._clusterSets.length) {
-                for (let i = this._clusterSets.length; i <= doc.cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
-                this._clusterSets[doc.cluster ?? 0].push(doc);
+                for (let i = this._clusterSets.length; i <= doc.layout_cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
+                this._clusterSets[doc.layout_cluster ?? 0].push(doc);
             }
         }
     }
@@ -566,8 +571,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props'  used to be renderDepth + 1
         switch (property) {
             case StyleProp.BackgroundColor:
-                const cluster = NumCast(doc?.cluster);
-                if (this.Document._useClusters && doc?.type !== DocumentType.IMG) {
+                const cluster = NumCast(doc?.layout_cluster);
+                if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
                     if (this._clusterSets.length <= cluster) {
                         setTimeout(() => doc && this.updateCluster(doc));
                     } else {
@@ -591,7 +596,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._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+            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);
             this.selectDocuments(eles);
             return true;
         }
@@ -678,7 +683,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(
                     ActiveInkColor(),
-                    Doc.ActiveTool,
                     ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
                     ActiveInkBezierApprox(),
                     ActiveFillColor(),
@@ -834,7 +838,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
             if (!this._deleteList.includes(intersect.inkView)) {
                 this._deleteList.push(intersect.inkView);
-                SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1');
+                SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.stroke_width?.toString()) || '1');
                 SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
                 // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
                 if (!e.shiftKey) {
@@ -1021,19 +1025,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (deltaScale * invTransform.Scale > 20) {
             deltaScale = 20 / invTransform.Scale;
         }
-        if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
+        if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._freeform_scaleMin, 1) && this.isAnnotationOverlay) {
             this.setPan(0, 0);
             return;
         }
-        if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
-            deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale;
+        if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._freeform_scaleMin, 1) && this.isAnnotationOverlay) {
+            deltaScale = NumCast(this.rootDoc._freeform_scaleMin, 1) / invTransform.Scale;
         }
 
         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.scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
+            this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
         }
     };
 
@@ -1113,26 +1117,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) {
             this.setPanZoomTransition(panTime);
             const scale = this.getLocalTransform().inverse().Scale;
-            const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
-            const minPanX = NumCast(this.rootDoc._panXMin, 0);
-            const minPanY = NumCast(this.rootDoc._panYMin, 0);
-            const newPanX = Math.min(minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
+            const minScale = NumCast(this.rootDoc._freeform_scaleMin, 1);
+            const minPanX = NumCast(this.rootDoc._freeform_panXMin, 0);
+            const minPanY = NumCast(this.rootDoc._freeform_panYMin, 0);
+            const newPanX = Math.min(minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._freeform_panXMax, this.nativeWidth), Math.max(minPanX, panX));
             const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this.props.PanelWidth() - this.props.PanelHeight()) * this.props.ScreenToLocalTransform().Scale) / minScale;
             const nativeHeight = (this.props.PanelHeight() / this.props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
             const maxScrollTop = this.nativeHeight / this.props.ScreenToLocalTransform().Scale - this.props.PanelHeight();
             const maxPanY =
-                minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by fitWidth
+                minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by layout_fitWidth
                 (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, nativeHeight) +
                 (!this.props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
             let newPanY = Math.max(minPanY, Math.min(maxPanY, panY));
-            if (false && NumCast(this.rootDoc.scrollTop) && NumCast(this.rootDoc._viewScale, minScale) !== minScale) {
-                const relTop = NumCast(this.rootDoc.scrollTop) / maxScrollTop;
-                this.rootDoc.scrollTop = undefined;
+            if (false && NumCast(this.rootDoc.layout_scrollTop) && NumCast(this.rootDoc._freeform_scale, minScale) !== minScale) {
+                const relTop = NumCast(this.rootDoc.layout_scrollTop) / maxScrollTop;
+                this.rootDoc.layout_scrollTop = undefined;
                 newPanY = minPanY + relTop * (maxPanY - minPanY);
-            } else if (fitYscroll && this.rootDoc.scrollTop === undefined && NumCast(this.rootDoc._viewScale, minScale) === minScale) {
+            } else if (fitYscroll && this.rootDoc.layout_scrollTop === undefined && NumCast(this.rootDoc._freeform_scale, minScale) === minScale) {
                 const maxPanY = minPanY + fitYscroll;
                 const relTop = (panY - minPanY) / (maxPanY - minPanY);
-                setTimeout(() => (this.rootDoc.scrollTop = relTop * maxScrollTop), 10);
+                setTimeout(() => (this.rootDoc.layout_scrollTop = relTop * maxScrollTop), 10);
                 newPanY = minPanY;
             }
             !this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX);
@@ -1143,7 +1147,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     nudge = (x: number, y: number, nudgeTime: number = 500) => {
         const collectionDoc = this.props.docViewPath().lastElement().rootDoc;
-        if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._panX !== undefined) {
+        if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._freeform_ !== undefined) {
             this.setPan(
                 NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
                 NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(),
@@ -1162,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
             let zfirst = docs.length ? NumCast(docs[0].zIndex) : 0;
             doc.zIndex = zfirst - 1;
-        } else if (doc.isInkMask) {
+        } else if (doc.stroke_isInkMask) {
             doc.zIndex = 5000;
         } else {
             const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
@@ -1222,8 +1226,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const panelWidth = this.props.isAnnotationOverlay ? this.nativeWidth : this.props.PanelWidth();
         const panelHeight = this.props.isAnnotationOverlay ? this.nativeHeight : this.props.PanelHeight();
-        const pw = panelWidth / NumCast(this.layoutDoc._viewScale, 1);
-        const ph = panelHeight / NumCast(this.layoutDoc._viewScale, 1);
+        const pw = panelWidth / NumCast(this.layoutDoc._freeform_scale, 1);
+        const ph = panelHeight / NumCast(this.layoutDoc._freeform_scale, 1);
         const cx = NumCast(this.layoutDoc[this.panXFieldKey]) + (this.props.isAnnotationOverlay ? pw / 2 : 0);
         const cy = NumCast(this.layoutDoc[this.panYFieldKey]) + (this.props.isAnnotationOverlay ? ph / 2 : 0);
         const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
@@ -1248,17 +1252,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._singleLine) && ['Tab', 'Enter'].includes(e.key)) {
+        if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== 'Tab';
-            const layoutKey = StrCast(docView.LayoutFieldKey);
+            const layout_fieldKey = StrCast(docView.LayoutFieldKey);
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
             if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
             else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
-            if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
-                newDoc[layoutKey] = docView.rootDoc[layoutKey];
+            if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
+                newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -1315,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 sizeProvider={this.childSizeProvider}
                 dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
                 bringToFront={this.bringToFront}
-                showTitle={this.props.childShowTitle}
+                layout_showTitle={this.props.childlayout_showTitle}
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
                 pointerEvents={this.pointerEvents}
                 //rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
@@ -1329,7 +1333,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case OpenWhere.inParent:
                 return this.props.addDocument?.(doc) || false;
             case OpenWhere.inParentFromScreen:
-                const docContext = DocCast((doc instanceof Doc ? doc : doc?.[0])?.context);
+                const docContext = DocCast((doc instanceof Doc ? doc : doc?.[0])?.embedContainer);
                 return (
                     (this.addDocument?.(
                         (doc instanceof Doc ? [doc] : doc).map(doc => {
@@ -1521,7 +1525,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 elements.push({
                     ele: this.getChildDocView(entry[1]),
                     bounds: childData.opacity === 0 ? { ...childData, width: 0, height: 0 } : { ...childData, width: childSize.width, height: childSize.height },
-                    inkMask: BoolCast(entry[1].pair.layout.isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
+                    inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
                 });
             });
 
@@ -1531,7 +1535,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             else this.props.Document[this.scaleFieldKey] = Math.max(1, this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
         }
 
-        this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
+        this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
         return elements;
     }
 
@@ -1541,10 +1545,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document._isGroup, viewType: true, filters: true } }, this.rootDoc);
 
         if (addAsAnnotation) {
-            if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
-                Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+            if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
+                Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
             } else {
-                this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+                this.dataDoc[this.props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
             }
         }
         return anchor;
@@ -1644,8 +1648,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             '',
             (iconFile, nativeWidth, nativeHeight) => {
                 this.dataDoc.icon = new ImageField(iconFile);
-                this.dataDoc['icon-nativeWidth'] = nativeWidth;
-                this.dataDoc['icon-nativeHeight'] = nativeHeight;
+                this.dataDoc['icon_nativeWidth'] = nativeWidth;
+                this.dataDoc['icon_nativeHeight'] = nativeHeight;
             }
         );
 
@@ -1762,7 +1766,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         appearanceItems.push({ description: (Doc.UserDoc().defaultToFlashcards ? 'Disable' : 'Enable') + ' Flashcard Notes', event: () => (Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards), icon: 'eye' });
         appearanceItems.push({
             description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`,
-            event: () => (this.Document._fitContentsToBox = !this.fitContentsToBox),
+            event: () => (this.Document._freeform_fitContentsToBox = !this.fitContentsToBox),
             icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt',
         });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
@@ -1781,7 +1785,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         !Doc.noviceMode
             ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? 'Hide' : 'Show') + ' Snap Lines', event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: 'compress-arrows-alt' })
             : null;
-        !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._useClusters), icon: 'braille' }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' });
 
         const options = ContextMenu.Instance.findByDescription('Options...');
@@ -1897,7 +1901,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
                     }}
                     style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
-                    {this.layoutDoc._backgroundGridShow ? (
+                    {this.layoutDoc._freeform_backgroundGrid ? (
                         <div>
                             <CollectionFreeFormBackgroundGrid // bcz : UGHH  don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
                                 PanelWidth={this.props.PanelWidth}
@@ -1937,7 +1941,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
-        return wscale < hscale || this.layoutDoc.fitWidth ? wscale : hscale;
+        return wscale < hscale || this.layoutDoc.layout_fitWidth ? wscale : hscale;
     }
     nativeDim = () => this.nativeDimScaling;
 
@@ -2310,7 +2314,7 @@ ScriptingGlobals.add(function sendToBack(doc: Doc) {
 });
 ScriptingGlobals.add(function resetView() {
     SelectionManager.Docs().forEach(doc => {
-        doc._panX = doc._panY = 0;
-        doc._viewScale = 1;
+        doc._freeform_panX = doc._freeform_panY = 0;
+        doc._freeform_scale = 1;
     });
 });
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 11d466b0f..fdff13b54 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -52,8 +52,8 @@ export interface MarqueeViewBounds {
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
     public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) {
-        const ps = NumCast(pinDoc._viewScale, 1);
-        return { left: NumCast(pinDoc._panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
+        const ps = NumCast(pinDoc._freeform_scale, 1);
+        return { left: NumCast(pinDoc._freeform_panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._freeform_panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
     }
 
     private _commandExecuted = false;
@@ -110,7 +110,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const cm = ContextMenu.Instance;
         const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
         if (e.key === '?') {
-            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), OpenWhere.addRight));
+            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
 
             cm.displayMenu(this._downX, this._downY, undefined, true);
             e.stopPropagation();
@@ -196,7 +196,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             const rowProto = new Doc();
             rowProto.title = rowProto.Id;
             rowProto._width = 200;
-            rowProto.isPrototype = true;
+            rowProto.isDataDoc = true;
             for (let i = 1; i < ns.length - 1; i++) {
                 const values = ns[i].split('\t');
                 if (values.length === 1 && columns.length > 1) {
@@ -204,7 +204,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                     continue;
                 }
                 const docDataProto = Doc.MakeDelegate(rowProto);
-                docDataProto.isPrototype = true;
+                docDataProto.isDataDoc = true;
                 columns.forEach((col, i) => (docDataProto[columns[i]] = values.length > i ? (values[i].indexOf(Number(values[i]).toString()) !== -1 ? Number(values[i]) : values[i]) : undefined));
                 if (groupAttr) {
                     docDataProto._group = groupAttr;
@@ -379,10 +379,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                   Doc.GetProto(doc).data = new List<Doc>(selected);
                   Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
                   !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc));
-                  doc._panX = doc._panY = 0;
+                  doc._freeform_panX = doc._freeform_panY = 0;
                   return doc;
               })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
-        newCollection.system = undefined;
+        newCollection.isSystem = undefined;
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
@@ -390,8 +390,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.enableDragWhenActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
-        newCollection.fitWidth = true;
-        selected.forEach(d => (d.context = newCollection));
+        newCollection.layout_fitWidth = true;
+        selected.forEach(d => (d.embedContainer = newCollection));
         this.hideMarquee();
         return newCollection;
     });
@@ -516,9 +516,19 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, followLinkToggle: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
+        const summary = Docs.Create.TextDocument('', {
+            backgroundColor: '#e2ad32',
+            x: this.Bounds.left,
+            y: this.Bounds.top,
+            followLinkToggle: true,
+            _width: 200,
+            _height: 200,
+            _layoutFitContentsToBox: true,
+            _layout_showSidebar: true,
+            title: 'overview',
+        });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
-        DocUtils.MakeLink(summary, portal, { linkRelationship: 'summary of:summarized by' });
+        DocUtils.MakeLink(summary, portal, { link_relationship: 'summary of:summarized by' });
 
         portal.hidden = true;
         this.props.addDocument?.(portal);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 588affc1c..bcc2ca129 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -79,7 +79,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _selectedCell: [Doc, number] | undefined;
 
     @computed get _selectedDocs() {
-        return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.rootDoc));
+        return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
     }
 
     @computed get documentKeys() {
@@ -87,7 +87,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get previewWidth() {
-        return NumCast(this.layoutDoc.schemaPreviewWidth);
+        return NumCast(this.layoutDoc.schema_previewWidth);
     }
 
     @computed get tableWidth() {
@@ -95,12 +95,12 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get columnKeys() {
-        return Cast(this.layoutDoc.columnKeys, listSpec('string'), defaultColumnKeys);
+        return Cast(this.layoutDoc.schema_columnKeys, listSpec('string'), defaultColumnKeys);
     }
 
     @computed get storedColumnWidths() {
         const widths = NumListCast(
-            this.layoutDoc.columnWidths,
+            this.layoutDoc.schema_columnWidths,
             this.columnKeys.map(() => (this.tableWidth - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
         );
 
@@ -243,7 +243,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
         let currKeys = [...this.columnKeys];
         currKeys[index] = newKey;
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
     };
 
     @undoBatch
@@ -257,11 +257,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         const currWidths = this.storedColumnWidths.slice();
         currWidths.splice(0, 0, newColWidth);
         const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
-        this.layoutDoc.columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
+        this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
 
         let currKeys = this.columnKeys.slice();
         currKeys.splice(0, 0, key);
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
     };
 
     @action
@@ -274,11 +274,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         const currWidths = this.storedColumnWidths.slice();
         currWidths.splice(index, 1);
         const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
-        this.layoutDoc.columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
+        this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
 
         let currKeys = this.columnKeys.slice();
         currKeys.splice(index, 1);
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
     };
 
     @action
@@ -317,7 +317,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     finishResize = () => {
-        this.layoutDoc.columnWidths = new List<number>(this._displayColumnWidths);
+        this.layoutDoc.schema_columnWidths = new List<number>(this._displayColumnWidths);
         this._displayColumnWidths = undefined;
     };
 
@@ -326,11 +326,11 @@ export class CollectionSchemaView extends CollectionSubView() {
     moveColumn = (fromIndex: number, toIndex: number) => {
         let currKeys = this.columnKeys.slice();
         currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
 
         let currWidths = this.storedColumnWidths.slice();
         currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]);
-        this.layoutDoc.columnWidths = new List<number>(currWidths);
+        this.layoutDoc.schema_columnWidths = new List<number>(currWidths);
     };
 
     @action
@@ -480,14 +480,14 @@ export class CollectionSchemaView extends CollectionSubView() {
         const maxWidth = 1000;
         const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
         const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
-        this.layoutDoc.schemaPreviewWidth = width;
+        this.layoutDoc.schema_previewWidth = width;
         return false;
     };
 
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
-        const newDoc = Docs.Create.TextDocument(value, { title: value, _autoHeight: true });
+        const newDoc = Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true });
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
         return this.addRow(newDoc) || false;
@@ -500,7 +500,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            this.addRow(Docs.Create.TextDocument('', { title: name, _autoHeight: true }));
+            this.addRow(Docs.Create.TextDocument('', { title: name, _layout_autoHeight: true }));
         });
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
@@ -837,7 +837,7 @@ export class CollectionSchemaView extends CollectionSubView() {
               });
         return { docs };
     }
-    rowHeightFunc = () => (BoolCast(this.layoutDoc._singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
+    rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
     sortedDocsFunc = () => this.sortedDocs;
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
@@ -968,7 +968,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 hideTitle={true}
                                 hideDocumentButtonBar={true}
                                 hideLinkAnchors={true}
-                                fitWidth={returnTrue}
+                                layout_fitWidth={returnTrue}
                                 scriptContext={this}
                                 canEmbedOnDrag={true}
                             />
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 34631e0b7..45bfe4f77 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -59,7 +59,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onPointerMove = (e: any) => {
         if (!SnappingManager.GetIsDragging()) return;
-        const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.context === this.schemaDoc); //  this.schemaView?._selectedDocs.has(doc) ?? false;
+        const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); //  this.schemaView?._selectedDocs.has(doc) ?? false;
 
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 9f659752a..42bf32475 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -73,7 +73,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'alias' as dropActionType,
+            dropAction: 'embed',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
@@ -83,7 +83,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             addDocTab: SchemaTableCell.addFieldDoc,
             pinToPres: returnZero,
             Document,
-            fieldKey,
+            fieldKey: fieldKey,
             PanelWidth: columnWidth,
             PanelHeight: props.rowHeight,
         };
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index d02a1c4eb..b9d56541a 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -24,12 +24,12 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
     private _menuRef = React.createRef<HTMLDivElement>();
 
     getBackgroundColor = (): string => {
-        const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList);
+        const link_relationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
         const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
         let color = 'white';
         // if this link's relationship property is not default "link", set its color
-        if (linkRelationshipList) {
-            const relationshipIndex = linkRelationshipList.indexOf(this.props.groupType);
+        if (link_relationshipList) {
+            const relationshipIndex = link_relationshipList.indexOf(this.props.groupType);
             const RGBcolor: string = linkColorList[relationshipIndex];
             if (RGBcolor) {
                 //set opacity to 0.25 by modifiying the rgb string
@@ -47,17 +47,18 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
             const sourceDoc =
                 this.props.docView.anchorViewDoc ??
                 (this.props.docView.rootDoc.type === DocumentType.LINK //
-                    ? this.props.docView.props.LayoutTemplateString?.includes('anchor1')
-                        ? DocCast(linkDoc.anchor1)
-                        : DocCast(linkDoc.anchor2)
+                    ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
+                        ? DocCast(linkDoc.link_anchor_1)
+                        : DocCast(linkDoc.link_anchor_2)
                     : this.props.sourceDoc);
             const destDoc = !sourceDoc
                 ? undefined
                 : this.props.docView.rootDoc.type === DocumentType.LINK
-                ? this.props.docView.props.LayoutTemplateString?.includes('anchor1')
-                    ? DocCast(linkDoc.anchor2)
-                    : DocCast(linkDoc.anchor1)
-                : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) || LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, 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/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 19d6c2ae2..5af05e491 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -8,15 +8,13 @@ import { Cast, DocCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
-import { DocumentManager } from '../../util/DocumentManager';
 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 { MainView } from '../MainView';
-import { DocumentView, OpenWhere } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
 import React = require('react');
@@ -32,7 +30,7 @@ interface LinkMenuItemProps {
     itemHandler?: (doc: Doc) => void;
 }
 
-// drag links and drop link targets (aliasing them if needed)
+// drag links and drop link targets (embedding them if needed)
 export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) {
     const draggedDocs = (specificLinks ? specificLinks : LinkManager.Links(sourceDoc)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[];
 
@@ -45,7 +43,7 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
 
         const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
         dragData.canEmbed = true;
-        dragData.dropAction = 'alias';
+        dragData.dropAction = 'embed';
 
         DragManager.StartDocumentDrag([dragEle], dragData, downX, downY, undefined);
     }
@@ -65,9 +63,9 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
     @computed get sourceAnchor() {
         const ldoc = this.props.linkDoc;
-        if (this.props.sourceDoc !== ldoc.anchor1 && this.props.sourceDoc !== ldoc.anchor2) {
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor1);
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor2);
+        if (this.props.sourceDoc !== ldoc.link_anchor_1 && this.props.sourceDoc !== ldoc.link_anchor_2) {
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_1);
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_2);
         }
         return this.props.sourceDoc;
     }
@@ -79,7 +77,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             this,
             e,
             e => {
-                const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'alias');
+                const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'embed');
                 dragData.removeDropProperties = ['hidden'];
                 DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
                 return true;
@@ -89,7 +87,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                 const trail = DocCast(this.props.docView.rootDoc.presTrail);
                 if (trail) {
                     Doc.ActivePresentation = trail;
-                    MainView.addDocTabFunc(trail, OpenWhere.replaceRight);
+                    DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight);
                 } else {
                     SelectionManager.SelectView(this.props.docView, false);
                     if ((SettingsManager.propertiesWidth ?? 0) < 100) {
@@ -118,10 +116,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                     this.props.itemHandler?.(this.props.linkDoc);
                 } else {
                     const focusDoc =
-                        Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc
-                            ? Cast(this.props.linkDoc.anchor1, Doc, null)
-                            : Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc
-                            ? Cast(this.props.linkDoc.anchor12, Doc, null)
+                        Cast(this.props.linkDoc.link_anchor_1, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.link_anchor_1, Doc, null)
+                            : Cast(this.props.linkDoc.link_anchor_2, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.link_anchor_12, Doc, null)
                             : undefined;
 
                     if (focusDoc) this.props.docView.props.focus(focusDoc, { instant: true });
@@ -149,7 +147,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
         return (
             <div className="linkMenu-item" style={{ background: LinkManager.currentLink === this.props.linkDoc ? 'lightBlue' : undefined }}>
-                <div className={'linkMenu-item-content expand-two'}>
+                <div className="linkMenu-item-content expand-two">
                     <div
                         ref={this._drag}
                         className="linkMenu-name" //title="drag to view target. click to customize."
@@ -190,7 +188,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                                     {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
                                 </p>
                             </div>
-                            {!this.props.linkDoc.description ? null : <p className="linkMenu-description">{StrCast(this.props.linkDoc.description)}</p>}
+                            {!this.props.linkDoc.link_description ? null : <p className="linkMenu-description">{StrCast(this.props.linkDoc.link_description)}</p>}
                         </div>
 
                         <div className="linkMenu-item-buttons">
diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx
index 53da880e4..9662b2fea 100644
--- a/src/client/views/linking/LinkRelationshipSearch.tsx
+++ b/src/client/views/linking/LinkRelationshipSearch.tsx
@@ -1,8 +1,8 @@
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import './LinkEditor.scss';
-import React = require("react");
+import React = require('react');
 
-interface LinkRelationshipSearchProps {
+interface link_relationshipSearchProps {
     results: string[] | undefined;
     display: string;
     //callback fn to set rel + hide dropdown upon setting
@@ -10,48 +10,48 @@ interface LinkRelationshipSearchProps {
     toggleSearch: () => void;
 }
 @observer
-export class LinkRelationshipSearch extends React.Component<LinkRelationshipSearchProps> {
-
+export class link_relationshipSearch extends React.Component<link_relationshipSearchProps> {
     handleResultClick = (e: React.MouseEvent) => {
         const relationship = (e.target as HTMLParagraphElement).textContent;
         if (relationship) {
             this.props.handleRelationshipSearchChange(relationship);
         }
-    }
+    };
 
     handleMouseEnter = () => {
         this.props.toggleSearch();
-    }
+    };
 
     handleMouseLeave = () => {
         this.props.toggleSearch();
-    }
+    };
 
     /**
-     * Render an empty div to increase the height of LinkEditor to accommodate 2+ results 
-    */
+     * Render an empty div to increase the height of LinkEditor to accommodate 2+ results
+     */
     emptyDiv = () => {
-        if (this.props.results && this.props.results.length > 2 && this.props.display === "block") {
-            return <div style={{ height: "50px" }} />;
+        if (this.props.results && this.props.results.length > 2 && this.props.display === 'block') {
+            return <div style={{ height: '50px' }} />;
         }
-    }
+    };
 
     render() {
         return (
             <div className="linkEditor-relationship-dropdown-container">
-                <div className="linkEditor-relationship-dropdown"
-                    style={{ display: this.props.display }}
-                    onMouseEnter={this.handleMouseEnter}
-                    onMouseLeave={this.handleMouseLeave}
-                >
-                    {   // return a dropdown of relationship results if there exist results
-                        this.props.results
-                            ? this.props.results.map(result => {
-                                return <p key={result} onClick={this.handleResultClick}>
-                                    {result}
-                                </p>;
+                <div className="linkEditor-relationship-dropdown" style={{ display: this.props.display }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+                    {
+                        // return a dropdown of relationship results if there exist results
+                        this.props.results ? (
+                            this.props.results.map(result => {
+                                return (
+                                    <p key={result} onClick={this.handleResultClick}>
+                                        {result}
+                                    </p>
+                                );
                             })
-                            : <p>No matching relationships</p>
+                        ) : (
+                            <p>No matching relationships</p>
+                        )
                     }
                 </div>
 
@@ -60,4 +60,4 @@ export class LinkRelationshipSearch extends React.Component<LinkRelationshipSear
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 68fb19208..8e83cf121 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -76,7 +76,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return DateCast(this.dataDoc[this.fieldKey + '-recordingStart'])?.date.getTime();
     }
     @computed get rawDuration() {
-        return NumCast(this.dataDoc[`${this.fieldKey}-duration`]);
+        return NumCast(this.dataDoc[`${this.fieldKey}_duration`]);
     } // bcz: shouldn't be needed since it's computed from audio element
     // mehek: not 100% sure but i think due to the order in which things are loaded this is necessary ^^
     // if you get rid of it and set the value to 0 the timeline and waveform will set their bounds incorrectly
@@ -123,12 +123,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     getLinkData(l: Doc) {
-        let la1 = l.anchor1 as Doc;
-        let la2 = l.anchor2 as Doc;
+        let la1 = l.link_anchor_1 as Doc;
+        let la2 = l.link_anchor_2 as Doc;
         const linkTime = this.timeline?.anchorStart(la2) || this.timeline?.anchorStart(la1) || 0;
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
-            la1 = l.anchor2 as Doc;
-            la2 = l.anchor1 as Doc;
+            la1 = l.link_anchor_2 as Doc;
+            la2 = l.link_anchor_1 as Doc;
         }
         return { la1, la2, linkTime };
     }
@@ -139,7 +139,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.rootDoc,
                 this.dataDoc,
                 this.annotationKey,
-                this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
+                this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
                 undefined,
                 undefined,
                 addAsAnnotation
@@ -155,12 +155,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this.links
                 .map(l => this.getLinkData(l))
                 .forEach(({ la1, la2, linkTime }) => {
-                    if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < this._ele!.currentTime) {
+                    if (linkTime > NumCast(this.layoutDoc._layout_currentTimecode) && linkTime < this._ele!.currentTime) {
                         Doc.linkFollowHighlight(la1);
                     }
                 });
-            this.layoutDoc._currentTimecode = this._ele.currentTime;
-            this.timeline?.scrollToTime(NumCast(this.layoutDoc._currentTimecode));
+            this.layoutDoc._layout_currentTimecode = this._ele.currentTime;
+            this.timeline?.scrollToTime(NumCast(this.layoutDoc._layout_currentTimecode));
         }
     };
 
@@ -221,7 +221,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (this.mediaState === media_state.Recording) {
             setTimeout(this.updateRecordTime, 30);
             if (!this._paused) {
-                this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this._pausedTime) / 1000;
+                this.layoutDoc._layout_currentTimecode = (new Date().getTime() - this._recordStart - this._pausedTime) / 1000;
             }
         }
     };
@@ -253,7 +253,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._recorder = undefined;
             const now = new Date().getTime();
             this._paused && (this._pausedTime += now - this._pauseStart);
-            this.dataDoc[this.fieldKey + '-duration'] = (now - this._recordStart - this._pausedTime) / 1000;
+            this.dataDoc[this.fieldKey + '_duration'] = (now - this._recordStart - this._pausedTime) / 1000;
             this.mediaState = media_state.Paused;
             this._stream?.getAudioTracks()[0].stop();
             const ind = DocUtils.ActiveRecordings.indexOf(this);
@@ -422,11 +422,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
                 this.playFrom(this.timeline?.anchorStart(link) || 0, this.timeline?.anchorEnd(link));
             } else {
-                this._ele!.currentTime = this.layoutDoc._currentTimecode = this.timeline?.anchorStart(link) || 0;
+                this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = this.timeline?.anchorStart(link) || 0;
             }
         } else {
             this.links
-                .filter(l => l.anchor1 === link || l.anchor2 === link)
+                .filter(l => l.link_anchor_1 === link || l.link_anchor_2 === link)
                 .forEach(l => {
                     const { la1, la2 } = this.getLinkData(l);
                     const startTime = this.timeline?.anchorStart(la1) || this.timeline?.anchorStart(la2);
@@ -435,7 +435,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
                             this.playFrom(startTime, endTime);
                         } else {
-                            this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime;
+                            this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = startTime;
                         }
                     }
                 });
@@ -447,7 +447,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     timelineScreenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -AudioBox.topControlsHeight);
 
-    setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._currentTimecode = time);
+    setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = time);
 
     playing = () => this.mediaState === media_state.Playing;
 
@@ -547,7 +547,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
                             <FontAwesomeIcon size="2x" icon={this._paused ? 'play' : 'pause'} />
                         </div>
-                        <div className="record-timecode">{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}</div>
+                        <div className="record-timecode">{formatTime(Math.round(NumCast(this.layoutDoc._layout_currentTimecode)))}</div>
                     </div>
                 ) : (
                     <div className="audiobox-start-record" onPointerDown={this.Record}>
@@ -623,7 +623,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 {this.audio}
 
                 <div className="audiobox-timecodes">
-                    <div className="timecode-current">{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}</div>
+                    <div className="timecode-current">{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._layout_currentTimecode) - NumCast(this.timeline.clipStart)))}</div>
                     {this.miniPlayer ? (
                         <div>/</div>
                     ) : (
@@ -656,6 +656,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 ref={action((r: any) => (this._stackedTimeline = r))}
                 {...this.props}
                 CollectionFreeFormDocumentView={undefined}
+                dataFieldKey={this.fieldKey}
                 fieldKey={this.annotationKey}
                 dictationKey={this.fieldKey + '-dictation'}
                 mediaPath={this.path}
@@ -689,7 +690,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             <audio
                 ref={this.setRef}
                 className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}
-                onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '-duration'] = this._ele.duration))}>
+                onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '_duration'] = this._ele.duration))}>
                 <source src={this.path} type="audio/mpeg" />
                 Not supported.
             </audio>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 862d0da8c..6710cee63 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -36,13 +36,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         { key: 'x' },
         { key: 'y' },
         { key: '_rotation', val: 0 },
-        { key: '_scrollTop' },
+        { key: '_layout_scrollTop' },
         { key: 'opacity', val: 1 },
         { key: '_currentFrame' },
-        { key: 'viewScale', val: 1 },
-        { key: 'viewScale', val: 1 },
-        { key: 'panX' },
-        { key: 'panY' },
+        { key: 'freeform_scale', val: 1 },
+        { key: 'freeform_scale', val: 1 },
+        { key: 'freeform_panX' },
+        { key: 'freeform_panY' },
     ]; // fields that are configured to be animatable using animation frames
     public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
     public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
@@ -154,8 +154,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
             CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (doc[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode)));
             const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so
-            //doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
-            targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0');
+            //doc !== targetDoc && (targetDoc.embedContainer = doc.embedContainer); // the computed fields don't see the layout doc -- need to copy the embedContainer to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
+            targetDoc.activeFrame = ComputedField.MakeFunction('self.embedContainer?._currentFrame||0');
             targetDoc.dataTransition = 'inherit';
         });
     }
@@ -202,7 +202,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
         };
-        const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString && !this.layoutDoc._isInkMask;
+        const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask;
         return (
             <div
                 className="collectionFreeFormDocumentView-container"
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 70ba7e182..330cc3971 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -48,7 +48,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     render() {
-        const scaling = Math.min(this.layoutDoc.fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]());
+        const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]());
         return (
             <div
                 className={`colorBox-container${this.props.isContentActive() ? '-interactive' : ''}`}
@@ -72,7 +72,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             SetActiveInkWidth(e.target.value);
                             SelectionManager.Views()
                                 .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK)
-                                .map(i => (i.rootDoc.strokeWidth = Number(e.target.value)));
+                                .map(i => (i.rootDoc.stroke_width = Number(e.target.value)));
                         }}
                     />
                 </div>
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index ace388c57..3be2d9a77 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
@@ -26,6 +26,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     @observable _animating = '';
 
+    @computed get clipWidth() {
+        return NumCast(this.layoutDoc[this.clipWidthKey], 50);
+    }
+    get clipWidthKey() {
+        return '_' + this.props.fieldKey + '_clipWidth';
+    }
+
     componentDidMount() {
         this.props.setContentView?.(this);
     }
@@ -59,7 +66,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                 action(() => {
                     // on click, animate slider movement to the targetWidth
                     this._animating = 'all 200ms';
-                    this.layoutDoc._clipWidth = (targetWidth * 100) / this.props.PanelWidth();
+                    this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth();
                     setTimeout(
                         action(() => (this._animating = '')),
                         200
@@ -71,9 +78,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     @action
     private onPointerMove = ({ movementX }: PointerEvent) => {
-        const width = movementX * this.props.ScreenToLocalTransform().Scale + (NumCast(this.layoutDoc._clipWidth) / 100) * this.props.PanelWidth();
+        const width = movementX * this.props.ScreenToLocalTransform().Scale + (this.clipWidth / 100) * this.props.PanelWidth();
         if (width && width > 5 && width < this.props.PanelWidth()) {
-            this.layoutDoc._clipWidth = (width * 100) / this.props.PanelWidth();
+            this.layoutDoc[this.clipWidthKey] = (width * 100) / this.props.PanelWidth();
         }
         return false;
     };
@@ -101,7 +108,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     };
 
     render() {
-        const clipWidth = NumCast(this.layoutDoc._clipWidth) + '%';
         const clearButton = (which: string) => {
             return (
                 <div
@@ -143,7 +149,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         };
         const displayBox = (which: string, index: number, cover: number) => {
             return (
-                <div className={`${which}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
+                <div className={`${index === 0 ? 'before' : 'after'}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
                     {displayDoc(which)}
                 </div>
             );
@@ -151,16 +157,16 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
         return (
             <div className={`comparisonBox${this.props.isContentActive() || SnappingManager.GetIsDragging() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
-                {displayBox(this.fieldKey === 'data' ? 'compareBox-after' : `${this.fieldKey}2`, 1, this.props.PanelWidth() - 3)}
-                <div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
-                    {displayBox(this.fieldKey === 'data' ? 'compareBox-before' : `${this.fieldKey}1`, 0, 0)}
+                {displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)}
+                <div className="clip-div" style={{ width: this.clipWidth + '%', transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
+                    {displayBox(`${this.fieldKey}_1`, 0, 0)}
                 </div>
 
                 <div
                     className="slide-bar"
                     style={{
-                        left: `calc(${clipWidth} - 0.5px)`,
-                        cursor: NumCast(this.layoutDoc._clipWidth) < 5 ? 'e-resize' : NumCast(this.layoutDoc._clipWidth) / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
+                        left: `calc(${this.clipWidth + '%'} - 0.5px)`,
+                        cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
                     }}
                     onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
                 >
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index eb25d3264..5876efb01 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, StrListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Cast, CsvCast, NumCast, StrCast } from '../../../../fields/Types';
 import { CsvField } from '../../../../fields/URLField';
 import { Docs } from '../../../documents/Documents';
 import { ViewBoxAnnotatableComponent } from '../../DocComponent';
@@ -29,7 +29,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // @observable private pairs: { [key: string]: FieldResult }[] = [];
     static pairSet = new ObservableMap<string, { [key: string]: string }[]>();
     @computed.struct get pairs() {
-        return DataVizBox.pairSet.get(StrCast(this.rootDoc.fileUpload));
+        return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href);
     }
     private _chartRenderer: LineChart | undefined;
     // // another way would be store a schema that defines the type of data we are expecting from an imported doc
@@ -61,7 +61,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @action
     restoreView = (data: Doc) => {
         const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView);
-        const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._dataVizAxes = new List<string>(StrListCast(data.presDataVizAxes)));
+        const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List<string>(StrListCast(data.presDataVizAxes)));
         const func = () => this._chartRenderer?.restoreView(data);
         if (changedView || changedAxes) {
             setTimeout(func, 100);
@@ -88,14 +88,14 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     @computed.struct get axes() {
-        return StrListCast(this.layoutDoc.dataVizAxes);
+        return StrListCast(this.layoutDoc.data_vizAxes);
     }
-    selectAxes = (axes: string[]) => (this.layoutDoc.dataVizAxes = new List<string>(axes));
+    selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List<string>(axes));
 
     @computed get selectView() {
         const width = this.props.PanelWidth() * 0.9;
         const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9;
-        const margin = { top: 10, right: 25, bottom: 50, left:25};
+        const margin = { top: 10, right: 25, bottom: 50, left: 25 };
         if (!this.pairs) return 'no data';
         // prettier-ignore
         switch (this.dataVizView) {
@@ -113,10 +113,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
 
     fetchData() {
-        if (DataVizBox.pairSet.has(StrCast(this.rootDoc.fileUpload))) return;
-        DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), []);
+        if (DataVizBox.pairSet.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) return;
+        DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []);
         fetch('/csvData?uri=' + this.dataUrl?.url.href) //
-            .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), res))));
+            .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res))));
     }
 
     // handle changing the view using a button
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 777bf2f66..11f62a61f 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -56,8 +56,8 @@ export class LineChart extends React.Component<LineChartProps> {
     }
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
-            .filter(link => link.anchor1 !== this.props.rootDoc) // get links where this chart doc is the target of the link
-            .map(link => DocCast(link.anchor1)); // then return the source of the link
+            .filter(link => link.link_anchor_1 !== this.props.rootDoc) // get links where this chart doc is the target of the link
+            .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
     @computed get incomingSelected() {
         return this.incomingLinks // all links that are pointing to this node
@@ -87,7 +87,7 @@ export class LineChart extends React.Component<LineChartProps> {
             { fireImmediately: true }
         );
         this._disposers.annos = reaction(
-            () => DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']),
+            () => DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']),
             annotations => {
                 // modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way
                 // could be blue colored to make it look like anchor
@@ -123,7 +123,7 @@ export class LineChart extends React.Component<LineChartProps> {
             element.classList.remove('selected');
         }
     };
-    // gets called whenever the "data-annotations" fields gets updated
+    // gets called whenever the "data_annotations" fields gets updated
     drawAnnotations = (dataX: number, dataY: number, selected?: boolean) => {
         // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements
         // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY
@@ -228,15 +228,15 @@ export class LineChart extends React.Component<LineChartProps> {
 
         // creating the x and y scales
         const xScale = scaleCreatorNumerical(xMin, xMax, 0, width);
-        const yScale = scaleCreatorNumerical(0, yMax,height, 0);
+        const yScale = scaleCreatorNumerical(0, yMax, height, 0);
 
         // adding svg
         const margin = this.props.margin;
         const svg = (this._lineChartSvg = d3
             .select(this._lineChartRef.current)
             .append('svg')
-            .attr('width', `${width +margin.left + margin.right}`)
-            .attr('height', `${height + margin.top + margin.bottom }`)
+            .attr('width', `${width + margin.left + margin.right}`)
+            .attr('height', `${height + margin.top + margin.bottom}`)
             .append('g')
             .attr('transform', `translate(${margin.left}, ${margin.top})`));
 
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 0d69ac890..3816bddfa 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -47,17 +47,17 @@ export class TableBox extends React.Component<TableBoxProps> {
                                                     e => {
                                                         const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
                                                         const targetCreator = (annotationOn: Doc | undefined) => {
-                                                            const alias = Doc.MakeAlias(this.props.docView?.()!.rootDoc!);
-                                                            alias._dataVizView = DataVizView.LINECHART;
-                                                            alias._dataVizAxes = new List<string>([col, col]);
-                                                            alias.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
-                                                            return alias;
+                                                            const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
+                                                            embedding._dataVizView = DataVizView.LINECHART;
+                                                            embedding._data_vizAxes = new List<string>([col, col]);
+                                                            embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
+                                                            return embedding;
                                                         };
                                                         if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
                                                             DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
                                                                 dragComplete: e => {
                                                                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
-                                                                        e.linkDocument.linkDisplay = true;
+                                                                        e.linkDocument.layout_linkDisplay = true;
                                                                         // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
                                                                         // e.annoDragData.linkSourceDoc.followLinkZoom = false;
                                                                     }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 76a5ce7b3..348ef910a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -120,15 +120,15 @@ export class DocumentContentsView extends React.Component<
         select: (ctrl: boolean) => void;
         NativeDimScaling?: () => number;
         setHeight?: (height: number) => void;
-        layoutKey: string;
+        layout_fieldKey: string;
     }
 > {
     @computed get layout(): string {
         TraceMobx();
         if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
         if (!this.layoutDoc) return '<p>awaiting layout</p>';
-        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString());
-        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, 'layout')], 'string');
+        if (this.props.layout_fieldKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString());
+        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layout_fieldKey ? this.props.layout_fieldKey : 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>';
@@ -140,12 +140,12 @@ export class DocumentContentsView extends React.Component<
     }
     get layoutDoc() {
         // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
-        // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+        // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined);
         const template: Doc =
             this.props.LayoutTemplate?.() ||
             (this.props.LayoutTemplateString && this.props.Document) ||
-            (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
-            Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+            (this.props.layout_fieldKey && StrCast(this.props.Document[this.props.layout_fieldKey]) && this.props.Document) ||
+            Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined);
         return Doc.expandTemplateLayout(template, this.props.Document);
     }
 
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 47705d53d..d5ca30957 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -52,7 +52,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                         dragComplete: dropEv => {
                             if (this.props.View && dropEv.linkDocument) {
                                 // dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
-                                !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = 'hyperlink');
+                                !dropEv.linkDocument.link_relationship && (Doc.GetProto(dropEv.linkDocument).link_relationship = 'hyperlink');
                             }
                             linkDrag?.end();
                         },
@@ -142,7 +142,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             } else if (startLink !== endLink) {
                 endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
                 startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
-                const linkDoc = DocUtils.MakeLink(startLink, endLink, { linkRelationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });
+                const linkDoc = DocUtils.MakeLink(startLink, endLink, { link_relationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });
 
                 LinkManager.currentLink = linkDoc;
 
@@ -197,7 +197,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         const results = [] as Doc[];
         const filters = this.props.View.props.docFilters();
         Array.from(new Set<Doc>(this.props.View.allLinks)).forEach(link => {
-            if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.anchor2 as Doc], filters, []).length || DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) {
+            if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.link_anchor_2 as Doc], filters, []).length || DocUtils.FilterDocs([link.link_anchor_1 as Doc], filters, []).length) {
                 results.push(link);
             }
         });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ffbde18ca..d8494e58e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -131,7 +131,7 @@ export interface DocComponentView {
     setData?: (data: Field | Promise<RefField | undefined>) => boolean;
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     incrementalRendering?: () => void;
-    fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
+    layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
     overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document
     fieldKey?: string;
     annotationKey?: string;
@@ -150,7 +150,7 @@ export interface DocumentViewSharedProps {
     Document: Doc;
     DataDoc?: Doc;
     contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
-    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
+    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
     suppressSetHeight?: boolean;
     thumbShown?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
@@ -164,11 +164,11 @@ export interface DocumentViewSharedProps {
     styleProvider: Opt<StyleProviderFunc>;
     setTitleFocus?: () => void;
     focus: DocFocusFunc;
-    fitWidth?: (doc: Doc) => boolean | undefined;
+    layout_fitWidth?: (doc: Doc) => boolean | undefined;
     docFilters: () => string[];
     docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
-    showTitle?: () => string;
+    layout_showTitle?: () => string;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
@@ -275,8 +275,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     public get LayoutFieldKey() {
         return Doc.LayoutFieldKey(this.layoutDoc);
     }
-    @computed get ShowTitle() {
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
+    @computed get layout_showTitle() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.layout_showTitle) as Opt<string>;
     }
     @computed get NativeDimScaling() {
         return this.props.NativeDimScaling?.() || 1;
@@ -305,8 +305,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get headerMargin() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
     }
-    @computed get showCaption() {
-        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.ShowCaption) || 0;
+    @computed get layout_showCaption() {
+        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.showCaption) || 0;
     }
     @computed get titleHeight() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
@@ -315,7 +315,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
     }
     @computed get finalLayoutKey() {
-        return StrCast(this.Document.layoutKey, 'layout');
+        return StrCast(this.Document.layout_fieldKey, 'layout');
     }
     @computed get nativeWidth() {
         return this.props.NativeWidth();
@@ -410,7 +410,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     // switches text input focus to the title bar of the document (and displays the title bar if it hadn't been)
     setTitleFocus = () => {
-        if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = 'title';
+        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
     };
 
@@ -539,7 +539,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
             this.cleanupPointerEvents();
-            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
+            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
         }
     };
 
@@ -589,7 +589,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
     @undoBatch setToggleDetail = () =>
         (this.Document.onClick = ScriptField.MakeScript(
-            `toggleDetail(documentView, "${StrCast(this.Document.layoutKey)
+            `toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
                 .replace('layout_', '')
                 .replace(/^layout$/, 'detail')}")`,
             { documentView: 'any' }
@@ -611,7 +611,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
                     de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
                 }
-                if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
+                if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.embedContainer) {
                     const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                 }
@@ -622,12 +622,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch
     @action
     makeIntoPortal = () => {
-        const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from');
+        const portalLink = this.allLinks.find(d => d.link_anchor_1 === this.props.Document && d.link_relationship === 'portal to:portal from');
         if (!portalLink) {
             DocUtils.MakeLink(
                 this.props.Document,
-                Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }),
-                { linkRelationship: 'portal to:portal from' }
+                Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _layout_fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }),
+                { link_relationship: 'portal to:portal from' }
             );
         }
         this.Document.followLinkLocation = OpenWhere.lightbox;
@@ -699,7 +699,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             .forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
 
         if (!this.props.Document.isFolder) {
-            const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
+            const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layout_fieldKey)], Doc, null);
             const appearance = cm.findByDescription('UI Controls...');
             const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
@@ -742,7 +742,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
             const funcs: ContextMenuProps[] = [];
             if (!Doc.noviceMode && this.layoutDoc.onDragStart) {
-                funcs.push({ description: 'Drag an Alias', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
+                funcs.push({ description: 'Drag an Embedding', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getEmbedding(this.dragFactory)')) });
                 funcs.push({ description: 'Drag a Copy', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
                 funcs.push({ description: 'Drag Document', icon: 'edit', event: () => (this.layoutDoc.onDragStart = undefined) });
                 cm.addItem({ description: 'OnDrag...', noexpand: true, subitems: funcs, icon: 'asterisk' });
@@ -910,7 +910,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     onClick={this.onClickFunc}
                     focus={this.props.focus}
                     setTitleFocus={this.setTitleFocus}
-                    layoutKey={this.finalLayoutKey}
+                    layout_fieldKey={this.finalLayoutKey}
                 />
                 {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
             </div>
@@ -922,7 +922,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         // prettier-ignore
         switch (property.split(':')[0]) {
-            case StyleProp.ShowTitle:     return '';
+            case StyleProp.layout_showTitle:     return '';
             case StyleProp.PointerEvents: return 'none';
             case StyleProp.Highlighting:  return undefined;
         }
@@ -938,22 +938,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         TraceMobx();
         return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(
             link =>
-                Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
-                Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
-                ((link.anchor1 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor1 as Doc)?.annotationOn as Doc, this.rootDoc)) ||
-                ((link.anchor2 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor2 as Doc)?.annotationOn as Doc, this.rootDoc))
+                Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.rootDoc) ||
+                Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.rootDoc) ||
+                ((link.link_anchor_1 as Doc)?.unrendered && Doc.AreProtosEqual((link.link_anchor_1 as Doc)?.annotationOn as Doc, this.rootDoc)) ||
+                ((link.link_anchor_2 as Doc)?.unrendered && Doc.AreProtosEqual((link.link_anchor_2 as Doc)?.annotationOn as Doc, this.rootDoc))
         );
     }
     @computed get allLinks() {
         TraceMobx();
         return LinkManager.Instance.getAllRelatedLinks(this.rootDoc);
     }
-    hideLink = computedFn((link: Doc) => () => (link.linkDisplay = false));
+    hideLink = computedFn((link: Doc) => () => (link.layout_linkDisplay = false));
     @computed get allLinkEndpoints() {
         // the small blue dots that mark the endpoints of links
         TraceMobx();
-        if (this.props.hideLinkAnchors || this.layoutDoc.hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.unrendered) return null;
-        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.linkDisplay);
+        if (this.props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.unrendered) return null;
+        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.layout_linkDisplay);
         return filtered.map(link => (
             <div className="documentView-anchorCont" key={link[Id]}>
                 <DocumentView
@@ -964,14 +964,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     PanelWidth={this.anchorPanelWidth}
                     PanelHeight={this.anchorPanelHeight}
                     dontRegisterView={false}
-                    showTitle={returnEmptyString}
+                    layout_showTitle={returnEmptyString}
                     hideCaptions={true}
                     hideLinkAnchors={true}
-                    fitWidth={returnTrue}
+                    layout_fitWidth={returnTrue}
                     removeDocument={this.hideLink(link)}
                     styleProvider={this.anchorStyleProvider}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(link, this.rootDoc)}`)}
+                    LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${Doc.LinkEndpoint(link, this.rootDoc)}`)}
                 />
             </div>
         ));
@@ -1046,9 +1046,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get innards() {
         TraceMobx();
         const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1;
-        const showTitle = this.ShowTitle?.split(':')[0];
-        const showTitleHover = this.ShowTitle?.includes(':hover');
-        const captionView = !this.showCaption ? null : (
+        const layout_showTitle = this.layout_showTitle?.split(':')[0];
+        const layout_showTitleHover = this.layout_showTitle?.includes(':hover');
+        const captionView = !this.layout_showCaption ? null : (
             <div
                 className="documentView-captionWrapper"
                 style={{
@@ -1060,7 +1060,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     {...this.props}
                     yPadding={10}
                     xPadding={10}
-                    fieldKey={this.showCaption}
+                    fieldKey={this.layout_showCaption}
                     fontSize={12 * Math.max(1, (2 * ffscale()) / 3)}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
@@ -1071,27 +1071,27 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 />
             </div>
         );
-        const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
+        const targetDoc = layout_showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
         const background = StrCast(
             SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
-            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)'
+            Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)'
         );
-        const sidebarWidthPercent = +StrCast(this.layoutDoc.sidebarWidthPercent).replace('%', '');
-        const titleView = !showTitle ? null : (
+        const layout_sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
+        const titleView = !layout_showTitle ? null : (
             <div
-                className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
+                className={`documentView-titleWrapper${layout_showTitleHover ? '-hover' : ''}`}
                 key="title"
                 style={{
                     position: this.headerMargin ? 'relative' : 'absolute',
                     height: this.titleHeight,
-                    width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button
+                    width: !this.headerMargin ? `calc(${layout_sidebarWidthPercent || 100}% - 18px)` : (layout_sidebarWidthPercent || 100) + '%', // leave room for annotation button
                     color: lightOrDark(background),
                     background,
                     pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                 }}>
                 <EditableView
                     ref={this._titleRef}
-                    contents={showTitle
+                    contents={layout_showTitle
                         .split(';')
                         .map(field => field.trim())
                         .map(field => targetDoc[field]?.toString())
@@ -1100,27 +1100,27 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     fontSize={10}
                     GetValue={() => {
                         this.props.select(false);
-                        return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle;
+                        return layout_showTitle.split(';').length === 1 ? layout_showTitle + '=' + Field.toString(targetDoc[layout_showTitle.split(';')[0]] as any as Field) : '#' + layout_showTitle;
                     }}
                     SetValue={undoBatch((input: string) => {
                         if (input?.startsWith('#')) {
-                            if (this.props.showTitle) {
-                                this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
+                            if (this.props.layout_showTitle) {
+                                this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined;
                             } else {
-                                Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : 'creationDate';
+                                Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'creationDate';
                             }
                         } else {
-                            var value = input.replace(new RegExp(showTitle + '='), '') as string | number;
-                            if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
-                            if (showTitle.includes('Date') || showTitle === 'author') return true;
-                            Doc.SetInPlace(targetDoc, showTitle, value, true);
+                            var value = input.replace(new RegExp(layout_showTitle + '='), '') as string | number;
+                            if (layout_showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
+                            if (layout_showTitle.includes('Date') || layout_showTitle === 'author') return true;
+                            Doc.SetInPlace(targetDoc, layout_showTitle, value, true);
                         }
                         return true;
                     })}
                 />
             </div>
         );
-        return this.props.hideTitle || (!showTitle && !this.showCaption) ? (
+        return this.props.hideTitle || (!layout_showTitle && !this.layout_showCaption) ? (
             this.contents
         ) : (
             <div className="documentView-styleWrapper">
@@ -1319,11 +1319,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     get LayoutFieldKey() {
         return this.docView?.LayoutFieldKey || 'layout';
     }
-    @computed get fitWidth() {
-        return this.docView?._componentView?.fitWidth?.() ?? this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
+    @computed get layout_fitWidth() {
+        return this.docView?._componentView?.layout_fitWidth?.() ?? this.props.layout_fitWidth?.(this.rootDoc) ?? this.layoutDoc?.layout_fitWidth;
     }
     @computed get anchorViewDoc() {
-        return this.props.LayoutTemplateString?.includes('anchor2') ? DocCast(this.rootDoc['anchor2']) : this.props.LayoutTemplateString?.includes('anchor1') ? DocCast(this.rootDoc['anchor1']) : undefined;
+        return this.props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.rootDoc['link_anchor_2']) : this.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.rootDoc['link_anchor_1']) : undefined;
     }
     @computed get hideLinkButton() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.isSelected() ? ':selected' : ''));
@@ -1339,13 +1339,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return Doc.Layout(this.Document, this.props.LayoutTemplate?.());
     }
     @computed get nativeWidth() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
     }
     @computed get nativeHeight() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
     }
     @computed get shouldNotScale() {
-        return (this.fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+        return (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._viewType as any);
     }
     @computed get effectiveNativeWidth() {
         return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width);
@@ -1356,8 +1356,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get nativeScaling() {
         if (this.shouldNotScale) return 1;
         const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
-        if (this.fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
-            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or fitWidth
+        if (this.layout_fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
+            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or layout_fitWidth
         }
         return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled
     }
@@ -1365,7 +1365,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth();
     }
     @computed get panelHeight() {
-        if (this.effectiveNativeHeight && (!this.fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) {
+        if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) {
             return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
         }
         return this.props.PanelHeight();
@@ -1374,7 +1374,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.effectiveNativeWidth ? Math.max(0, (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2) : 0;
     }
     @computed get Yshift() {
-        return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && (!this.layoutDoc.nativeHeightUnfrozen || (!this.fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
+        return this.effectiveNativeWidth &&
+            this.effectiveNativeHeight &&
+            Math.abs(this.Xshift) < 0.001 &&
+            (!this.layoutDoc.nativeHeightUnfrozen || (!this.layout_fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
             ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2)
             : 0;
     }
@@ -1410,10 +1413,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             finished?.();
             this.docView && (this.docView._animateScaleTime = animTime);
         });
-        const layoutKey = Cast(this.Document.layoutKey, 'string', null);
-        if (layoutKey !== 'layout_icon') {
+        const layout_fieldKey = Cast(this.Document.layout_fieldKey, 'string', null);
+        if (layout_fieldKey !== 'layout_icon') {
             this.switchViews(true, 'icon', finalFinished);
-            if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') this.Document.deiconifyLayout = layoutKey.replace('layout_', '');
+            if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') this.Document.deiconifyLayout = layout_fieldKey.replace('layout_', '');
         } else {
             const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
             this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished);
@@ -1433,7 +1436,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         setTimeout(
             action(() => {
                 if (useExistingLayout && custom && this.rootDoc['layout_' + view]) {
-                    this.rootDoc.layoutKey = 'layout_' + view;
+                    this.rootDoc.layout_fieldKey = 'layout_' + view;
                 } else {
                     this.setCustomView(custom, view);
                 }
@@ -1520,7 +1523,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             width: xshift ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
                             height: this.props.forceAutoHeight
                                 ? undefined
-                                : yshift ?? (this.fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
+                                : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
                         }}>
                         <DocumentViewInternal
                             {...this.props}
@@ -1558,7 +1561,7 @@ ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView
 });
 
 ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
-    if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
+    if (dv.Document.layout_fieldKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
     else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
 });
 
@@ -1571,12 +1574,12 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
         const other = LinkManager.getOppositeAnchor(link, linkSource);
         const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other;
         if (otherdoc && !collectedLinks?.some(d => Doc.AreProtosEqual(d, otherdoc))) {
-            const alias = Doc.MakeAlias(otherdoc);
-            alias.x = wid;
-            alias.y = 0;
-            alias._lockedPosition = false;
+            const embedding = Doc.MakeEmbedding(otherdoc);
+            embedding.x = wid;
+            embedding.y = 0;
+            embedding._lockedPosition = false;
             wid += otherdoc[WidthSym]();
-            Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', alias);
+            Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', embedding);
         }
     });
     return links;
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index 163c5a9ed..f17ab06e7 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -90,7 +90,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
     render() {
         TraceMobx();
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
         return (
             <div
                 ref={r => this.updateSize()}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 7eae4e938..85dd779fc 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,7 +50,7 @@ export class FieldView extends React.Component<FieldViewProps> {
 
     @computed
     get field(): FieldResult {
-        const { Document, fieldKey } = this.props;
+        const { Document, fieldKey: fieldKey } = this.props;
         return Document[fieldKey];
     }
     render() {
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index b43e359ff..db72209ab 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -90,7 +90,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc);
         }
-        // if (this.autoHeight) this.tryUpdateScrollHeight();
+        // if (this.layout_autoHeight) this.tryUpdateScrollHeight();
     };
     @computed get theGraph() {
         return <div id={`${this._plotId}`} ref={r => r && this.createGraph(r)} style={{ position: 'absolute', width: '100%', height: '100%' }} onPointerDown={e => e.stopPropagation()} />;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c9be10d3a..7d1d50cc7 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -79,9 +79,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
             Docs.Create.ImageanchorDocument({
                 title: 'ImgAnchor:' + this.rootDoc.title,
-                presPanX: NumCast(this.layoutDoc._panX),
-                presPanY: NumCast(this.layoutDoc._panY),
-                presViewScale: Cast(this.layoutDoc._viewScale, 'number', null),
+                presPanX: NumCast(this.layoutDoc._freeform_panX),
+                presPanY: NumCast(this.layoutDoc._freeform_panY),
+                presViewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
                 presTransition: 1000,
                 unrendered: true,
                 annotationOn: this.rootDoc,
@@ -99,7 +99,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._disposers.sizer = reaction(
             () => ({
                 forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
-                scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._viewScale, 1),
+                scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._freeform_scale, 1),
                 selected: this.props.isSelected(),
             }),
             ({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'),
@@ -116,7 +116,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(
-            () => this.layoutDoc._scrollTop,
+            () => this.layoutDoc.layout_scrollTop,
             s_top => {
                 this._forcedScroll = true;
                 !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(s_top));
@@ -144,7 +144,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 de.complete.docDragData.droppedDocuments.forEach(
                     action((drop: Doc) => {
                         Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
-                        this.rootDoc[this.fieldKey + '-usePath'] = 'alternate:hover';
+                        this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
                         e.stopPropagation();
                     })
                 );
@@ -167,28 +167,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @undoBatch
     setNativeSize = action(() => {
-        const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._viewScale, 1);
+        const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._freeform_scale, 1);
         const nscale = NumCast(this.props.PanelWidth()) / scaling;
-        const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
-        const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
-        this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh;
-        this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nh;
-        this.rootDoc._panX = nh * NumCast(this.rootDoc._panX);
-        this.rootDoc._panY = nh * NumCast(this.rootDoc._panY);
-        this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined;
-        this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined;
-        this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined;
-        this.dataDoc._panYMin = this.dataDoc._panYMin ? nw * NumCast(this.dataDoc._panYMin) : undefined;
+        const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']);
+        const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+        this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nh;
+        this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nh;
+        this.rootDoc._freeform_panX = nh * NumCast(this.rootDoc._freeform_panX);
+        this.rootDoc._freeform_panY = nh * NumCast(this.rootDoc._freeform_panY);
+        this.dataDoc._freeform_panXMax = this.dataDoc._freeform_panXMax ? nh * NumCast(this.dataDoc._freeform_panXMax) : undefined;
+        this.dataDoc._freeform_panXMin = this.dataDoc._freeform_panXMin ? nh * NumCast(this.dataDoc._freeform_panXMin) : undefined;
+        this.dataDoc._freeform_panYMax = this.dataDoc._freeform_panYMax ? nw * NumCast(this.dataDoc._freeform_panYMax) : undefined;
+        this.dataDoc._freeform_panYMin = this.dataDoc._freeform_panYMin ? nw * NumCast(this.dataDoc._freeform_panYMin) : undefined;
     });
     @undoBatch
     rotate = action(() => {
-        const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
-        const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
+        const nw = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+        const nh = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']);
         const w = this.layoutDoc._width;
         const h = this.layoutDoc._height;
         this.dataDoc[this.fieldKey + '-rotation'] = (NumCast(this.dataDoc[this.fieldKey + '-rotation']) + 90) % 360;
-        this.dataDoc[this.fieldKey + '-nativeWidth'] = nh;
-        this.dataDoc[this.fieldKey + '-nativeHeight'] = nw;
+        this.dataDoc[this.fieldKey + '_nativeWidth'] = nh;
+        this.dataDoc[this.fieldKey + '_nativeHeight'] = nw;
         this.layoutDoc._width = h;
         this.layoutDoc._height = w;
     });
@@ -204,7 +204,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const anchy = NumCast(cropping.y);
         const anchw = NumCast(cropping._width);
         const anchh = NumCast(cropping._height);
-        const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw;
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
@@ -213,24 +213,24 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
-        croppingProto.isPrototype = true;
+        croppingProto.isDataDoc = true;
         croppingProto.backgroundColor = undefined;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
         croppingProto.layout = ImageBox.LayoutString('data');
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
-        croppingProto['data-nativeWidth'] = anchw;
-        croppingProto['data-nativeHeight'] = anchh;
-        croppingProto.viewScale = viewScale;
-        croppingProto.viewScaleMin = viewScale;
-        croppingProto.panX = anchx / viewScale;
-        croppingProto.panY = anchy / viewScale;
-        croppingProto.panXMin = anchx / viewScale;
-        croppingProto.panXMax = anchw / viewScale;
-        croppingProto.panYMin = anchy / viewScale;
-        croppingProto.panYMax = anchh / viewScale;
+        croppingProto['data_nativeWidth'] = anchw;
+        croppingProto['data_nativeHeight'] = anchh;
+        croppingProto.freeform_scale = viewScale;
+        croppingProto.freeform_scaleMin = viewScale;
+        croppingProto.freeform_panX = anchx / viewScale;
+        croppingProto.freeform_panY = anchy / viewScale;
+        croppingProto.freeform_panXMin = anchx / viewScale;
+        croppingProto.freeform_panXMax = anchw / viewScale;
+        croppingProto.freeform_panYMin = anchy / viewScale;
+        croppingProto.freeform_panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
+            DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
             cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]();
             cropping.y = NumCast(this.rootDoc.y);
             this.props.addDocTab(cropping, OpenWhere.inParent);
@@ -312,7 +312,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
     };
 
-    getScrollHeight = () => (this.props.fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._viewScale, 1) === NumCast(this.rootDoc._viewScaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
+    getScrollHeight = () => (this.props.layout_fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._freeform_scale, 1) === NumCast(this.rootDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
 
     @computed
     private get considerDownloadIcon() {
@@ -358,13 +358,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @computed get nativeSize() {
         TraceMobx();
-        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth'], 500));
-        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight'], 500));
+        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500));
+        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500));
         const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '-nativeOrientation'], 1);
         return { nativeWidth, nativeHeight, nativeOrientation };
     }
     @computed get overlayImageIcon() {
-        const usePath = this.rootDoc[`_${this.fieldKey}-usePath`];
+        const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
         return (
             <Tooltip
                 title={
@@ -385,7 +385,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 <div
                     className="imageBox-alternateDropTarget"
                     ref={this._overlayIconRef}
-                    onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}-usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
+                    onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
                     style={{
                         display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
                         width: 'min(10%, 25px)',
@@ -431,7 +431,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             transformOrigin = 'right top';
             transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         }
-        const usePath = this.rootDoc[`_${this.fieldKey}-usePath`];
+        const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
 
         return (
             <div className="imageBox-cont" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
@@ -459,9 +459,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         TraceMobx();
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
-    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop) * this.props.ScreenToLocalTransform().Scale);
+    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.props.ScreenToLocalTransform().Scale);
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._freeform_scale, 1) <= NumCast(this.rootDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -497,9 +497,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 ref={this._mainCont}
                 onScroll={action(e => {
                     if (!this._forcedScroll) {
-                        if (this.layoutDoc._scrollTop || this._mainCont.current?.scrollTop) {
+                        if (this.layoutDoc._layout_scrollTop || this._mainCont.current?.scrollTop) {
                             this._ignoreScroll = true;
-                            this.layoutDoc._scrollTop = this._mainCont.current?.scrollTop;
+                            this.layoutDoc._layout_scrollTop = this._mainCont.current?.scrollTop;
                             this._ignoreScroll = false;
                         }
                     }
@@ -510,7 +510,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     height: this.props.PanelWidth() ? undefined : `100%`,
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
                     borderRadius,
-                    overflow: this.layoutDoc.fitWidth || this.props.fitWidth?.(this.rootDoc) ? 'auto' : undefined,
+                    overflow: this.layoutDoc.layout_fitWidth || this.props.layout_fitWidth?.(this.rootDoc) ? 'auto' : undefined,
                 }}>
                 <CollectionFreeFormView
                     ref={this._ffref}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index f3c8ba62d..88a82e8e6 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -46,15 +46,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
     reverseNativeScaling = returnTrue;
     able = returnAlways;
-    fitWidth = returnTrue;
+    layout_fitWidth = returnTrue;
     overridePointerEvents = returnAll;
     onClickScriptDisable = returnAlways;
 
     @observable private rows: KeyValuePair[] = [];
+    @observable _splitPercentage = 50;
 
-    @computed get splitPercentage() {
-        return NumCast(this.props.Document.schemaSplitPercentage, 50);
-    }
     get fieldDocToLayout() {
         return this.props.fieldKey ? DocCast(this.props.Document[this.props.fieldKey], DocCast(this.props.Document)) : this.props.Document;
     }
@@ -147,7 +145,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         const rows: JSX.Element[] = [];
         let i = 0;
         const self = this;
-        for (const key of Object.keys(ids).slice().sort()) {
+        const keys = Object.keys(ids).slice();
+        //for (const key of [...keys.filter(id => id !== 'layout' && !id.includes('_')).sort(), ...keys.filter(id => id === 'layout' || id.includes('_')).sort()]) {
+        for (const key of keys.sort()) {
             rows.push(
                 <KeyValuePair
                     doc={realDoc}
@@ -162,7 +162,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                             if (el) self.rows.push(el);
                         };
                     })()}
-                    keyWidth={100 - this.splitPercentage}
+                    keyWidth={100 - this._splitPercentage}
                     rowStyle={'keyValueBox-' + (i++ % 2 ? 'oddRow' : 'evenRow')}
                     key={key}
                     keyName={key}
@@ -180,7 +180,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                         this._keyInput.current!.select();
                         e.stopPropagation();
                     }}
-                    style={{ width: `${100 - this.splitPercentage}%` }}>
+                    style={{ width: `${100 - this._splitPercentage}%` }}>
                     <input style={{ width: '100%' }} ref={this._keyInput} type="text" placeholder="Key" />
                 </td>
                 <td
@@ -189,7 +189,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                         this._valInput.current!.select();
                         e.stopPropagation();
                     }}
-                    style={{ width: `${this.splitPercentage}%` }}>
+                    style={{ width: `${this._splitPercentage}%` }}>
                     <input style={{ width: '100%' }} ref={this._valInput} type="text" placeholder="Value" onKeyDown={this.onEnterKey} />
                 </td>
             </tr>
@@ -199,7 +199,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @action
     onDividerMove = (e: PointerEvent): void => {
         const nativeWidth = this._mainCont.current!.getBoundingClientRect();
-        this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100));
+        this._splitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100));
     };
     @action
     onDividerUp = (e: PointerEvent): void => {
@@ -216,7 +216,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     getFieldView = async () => {
         const rows = this.rows.filter(row => row.isChecked);
         if (rows.length > 1) {
-            const parent = Docs.Create.StackingDocument([], { _autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true });
+            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true });
             for (const row of rows) {
                 const field = this.createFieldView(DocCast(this.props.Document.data), row);
                 field && Doc.AddDocToList(parent, 'data', field);
@@ -229,9 +229,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
         const metaKey = row.props.keyName;
-        const fieldTemplate = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeAlias(templateDoc);
+        const fieldTemplate = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeEmbedding(templateDoc);
         fieldTemplate.title = metaKey;
-        fieldTemplate.fitWidth = true;
+        fieldTemplate.layout_fitWidth = true;
         fieldTemplate._xMargin = 10;
         fieldTemplate._yMargin = 10;
         fieldTemplate._width = 100;
@@ -284,8 +284,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     render() {
         const dividerDragger =
-            this.splitPercentage === 0 ? null : (
-                <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
+            this._splitPercentage === 0 ? null : (
+                <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this._splitPercentage}% - 5px), 0px)` }}>
                     <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
                 </div>
             );
@@ -295,10 +295,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                 <table className="keyValueBox-table">
                     <tbody className="keyValueBox-tbody">
                         <tr className="keyValueBox-header">
-                            <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }} ref={this._keyHeader} onPointerDown={SetupDrag(this._keyHeader, this.getFieldView)}>
+                            <th className="keyValueBox-key" style={{ width: `${100 - this._splitPercentage}%` }} ref={this._keyHeader} onPointerDown={SetupDrag(this._keyHeader, this.getFieldView)}>
                                 Key
                             </th>
-                            <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>
+                            <th className="keyValueBox-fields" style={{ width: `${this._splitPercentage}%` }}>
                                 Fields
                             </th>
                         </tr>
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 57d36932e..2b4867321 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -19,6 +19,7 @@
             position: relative;
             margin: 0;
         }
+        .keyValuePair-keyFieldMod,
         .keyValuePair-keyField {
             width: 100%;
             margin-left: 20px;
@@ -27,6 +28,9 @@
             overflow: auto;
             display: inline;
         }
+        .keyValuePair-keyFieldMod {
+            margin-left: 35px;
+        }
     }
 }
 .keyValuePair-td-value {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 85641124f..509bb667e 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -66,7 +66,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'alias',
+            dropAction: 'embed',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
@@ -108,8 +108,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                             })}>
                             X
                         </button>
-                        <input className={'keyValuePair-td-key-check'} type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
-                        <div className="keyValuePair-keyField" style={{ color: keyStyle }}>
+                        <input className="keyValuePair-td-key-check" type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
+                        <div className={`keyValuePair-keyField${props.fieldKey.includes('_') ? 'Mod' : ''}`} style={{ color: keyStyle }}>
                             {'('.repeat(parenCount)}
                             {props.fieldKey}
                             {')'.repeat(parenCount)}
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 916458dfd..32026ea9c 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.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);
     }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -82,7 +82,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
         return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
     }
 
-    fitTextToBox = (r: any): any => {
+    fitTextToBox = (r: any) => {
         const singleLine = BoolCast(this.rootDoc._singleLine, true);
         const params = {
             rotateText: null,
@@ -141,9 +141,9 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
                         paddingBottom: NumCast(this.rootDoc._yPadding),
                         width: this.props.PanelWidth(),
                         height: this.props.PanelHeight(),
-                        whiteSpace: boxParams.singleLine ? 'pre' : 'pre-wrap',
+                        whiteSpace: typeof boxParams !== 'number' && boxParams.singleLine ? 'pre' : 'pre-wrap',
                     }}>
-                    <span style={{ width: boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
+                    <span style={{ width: typeof boxParams !== 'number' && boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
                         {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')}
                     </span>
                 </div>
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 31f1775e5..f38ef634c 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -46,14 +46,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.rootDoc]);
-                dragData.dropAction = 'alias';
-                dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'onClick'];
+                dragData.dropAction = 'embed';
+                dragData.removeDropProperties = ['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.linkAutoMove = false;
+                this.rootDoc.layout_autoMoveAnchors = false;
             }
         }
         return false;
@@ -67,10 +67,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const x = NumCast(this.rootDoc[this.fieldKey + '_x'], 100);
         const y = NumCast(this.rootDoc[this.fieldKey + '_y'], 100);
         const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor');
-        const anchor = this.fieldKey === 'anchor1' ? 'anchor2' : 'anchor1';
-        const anchorScale = !this.dataDoc[this.fieldKey + '-useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
+        const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
+        const anchorScale = !this.dataDoc[this.fieldKey + '_useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
         const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
-        const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor1') ? 'anchor1' : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor2') ? 'anchor2' : '';
+        const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_1')
+            ? 'link_anchor_1'
+            : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_2')
+            ? 'link_anchor_2'
+            : '';
         return (
             <div
                 ref={this._ref}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 46ccdecae..710d41471 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -9,7 +9,7 @@ import './LinkBox.scss';
 
 @observer
 export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) {
+    public static LayoutString(fieldKey: string = 'link') {
         return FieldView.LayoutString(LinkBox, fieldKey);
     }
 
@@ -23,7 +23,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
             <div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
                 <ComparisonBox
                     {...this.props}
-                    fieldKey="anchor"
+                    fieldKey="link_anchor"
                     setHeight={emptyFunction}
                     dontRegisterView={true}
                     renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index 91bd505c5..c45045a8a 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -24,7 +24,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
     onDismiss = (add: boolean) => {
         LinkDescriptionPopup.descriptionPopup = false;
         if (add) {
-            LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).description = this.description);
+            LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).link_description = this.description);
         }
     };
 
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c58b5dd8c..c6172ee01 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -58,13 +58,13 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         var linkTarget = this.props.linkDoc;
         this._linkSrc = this.props.linkSrc;
         this._linkDoc = this.props.linkDoc;
-        const anchor1 = this._linkDoc?.anchor1 as Doc;
-        const anchor2 = this._linkDoc?.anchor2 as Doc;
-        if (anchor1 && anchor2) {
-            linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1;
+        const link_anchor_1 = this._linkDoc?.link_anchor_1 as Doc;
+        const link_anchor_2 = this._linkDoc?.link_anchor_2 as Doc;
+        if (link_anchor_1 && link_anchor_2) {
+            linkTarget = Doc.AreProtosEqual(link_anchor_1, this._linkSrc) || Doc.AreProtosEqual(link_anchor_1?.annotationOn as Doc, this._linkSrc) ? link_anchor_2 : link_anchor_1;
         }
         if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) {
-            // want to show annotation context document if annotation is not text
+            // want to show annotation embedContainer document if annotation is not text
             linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._markerTargetDoc = this._targetDoc = anno)));
         } else {
             this._markerTargetDoc = this._targetDoc = linkTarget;
@@ -111,7 +111,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                     action(anchor => {
                         if (anchor instanceof Doc && LinkManager.Links(anchor).length) {
                             this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0];
-                            const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
+                            const automaticLink = this._linkDoc.link_relationship === LinkManager.AutoKeywords;
                             if (automaticLink) {
                                 // automatic links specify the target in the link info, not the source
                                 const linkTarget = anchor;
@@ -175,7 +175,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         } else if (this.props.hrefs?.length) {
             const webDoc =
                 Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
-                Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true });
+                Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
             this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
         }
     };
@@ -208,7 +208,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 </div>
                 <div className="linkDocPreview-title" style={{ pointerEvents: 'all' }}>
                     {StrCast(this._markerTargetDoc.title).length > 16 ? StrCast(this._markerTargetDoc.title).substr(0, 16) + '...' : StrCast(this._markerTargetDoc.title)}
-                    <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
+                    <p className="linkDocPreview-description"> {StrCast(this._linkDoc.link_description)}</p>
                 </div>
                 <div className="linkDocPreview-buttonBar" style={{ float: 'right' }}>
                     <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
@@ -263,7 +263,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             isDocumentActive={returnFalse}
                             isContentActive={returnFalse}
                             addDocument={returnFalse}
-                            showTitle={returnEmptyString}
+                            layout_showTitle={returnEmptyString}
                             removeDocument={returnFalse}
                             addDocTab={returnFalse}
                             pinToPres={returnFalse}
@@ -278,7 +278,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             pointerEvents={returnNone}
                             focus={emptyFunction}
                             whenChildContentsActiveChanged={returnFalse}
-                            ignoreAutoHeight={true} // need to ignore autoHeight otherwise autoHeight text boxes will expand beyond the preview panel size.
+                            ignoreAutoHeight={true} // need to ignore layout_autoHeight otherwise layout_autoHeight text boxes will expand beyond the preview panel size.
                             bringToFront={returnFalse}
                             NativeWidth={Doc.NativeWidth(this._targetDoc) ? () => Doc.NativeWidth(this._targetDoc) : undefined}
                             NativeHeight={Doc.NativeHeight(this._targetDoc) ? () => Doc.NativeHeight(this._targetDoc) : undefined}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 36be7d257..9b0fddce4 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -98,7 +98,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return FieldView.LayoutString(MapBox, fieldKey);
     }
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
@@ -126,7 +126,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     static _canAnnotate = true;
@@ -267,7 +267,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setTimeout(() => {
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+                this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -282,7 +282,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -292,7 +292,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     };
@@ -327,7 +327,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         console.log('print all sidebar Docs');
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
             if (doc.lat !== undefined && doc.lng !== undefined) {
@@ -351,7 +351,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @returns
      */
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        if (this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         return this.removeDocument(doc, sidebarKey);
     };
@@ -375,11 +375,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     if (this.sidebarWidth() + localDelta[0] > 0) {
                         this._showSidebar = true;
                         this.layoutDoc._width = fullWidth + localDelta[0];
-                        this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
                     } else {
                         this._showSidebar = false;
                         this.layoutDoc._width = mapWidth;
-                        this.layoutDoc._sidebarWidthPercent = '0%';
+                        this.layoutDoc._layout_sidebarWidthPercent = '0%';
                     }
                     return false;
                 }),
@@ -388,12 +388,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     };
 
-    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
-    @computed get sidebarWidthPercent() {
-        return StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    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.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
 
     /**
@@ -453,7 +453,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={this.sidebarBtnDown}>
@@ -467,8 +467,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     toggleSidebar = () => {
         //1.2 * w * ? = .2 * w      .2/1.2
         const prevWidth = this.sidebarWidth();
-        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
-        this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+        this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+        this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
     sidebarDown = (e: React.PointerEvent) => {
@@ -476,8 +476,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
-        this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+        this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
     };
@@ -548,7 +548,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     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._scrollTop));
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
     infoWidth = () => this.props.PanelWidth() / 5;
@@ -616,7 +616,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         //     zoom: 15,
                         // });
                     }}
-                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+                    style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
@@ -667,7 +667,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 37f9bad9e..9354f9639 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -95,7 +95,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return FieldView.LayoutString(MapBox2, fieldKey);
     }
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
@@ -123,7 +123,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     static _canAnnotate = true;
@@ -264,7 +264,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setTimeout(() => {
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+                this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -279,7 +279,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -289,7 +289,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     };
@@ -324,7 +324,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         console.log('print all sidebar Docs');
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
             if (doc.lat !== undefined && doc.lng !== undefined) {
@@ -348,7 +348,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @returns
      */
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        if (this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         return this.removeDocument(doc, sidebarKey);
     };
@@ -372,11 +372,11 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     if (this.sidebarWidth() + localDelta[0] > 0) {
                         this._showSidebar = true;
                         this.layoutDoc._width = fullWidth + localDelta[0];
-                        this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
                     } else {
                         this._showSidebar = false;
                         this.layoutDoc._width = mapWidth;
-                        this.layoutDoc._sidebarWidthPercent = '0%';
+                        this.layoutDoc._layout_sidebarWidthPercent = '0%';
                     }
                     return false;
                 }),
@@ -385,12 +385,12 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     };
 
-    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
-    @computed get sidebarWidthPercent() {
-        return StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    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.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
 
     /**
@@ -450,7 +450,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={this.sidebarBtnDown}>
@@ -464,8 +464,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     toggleSidebar = () => {
         //1.2 * w * ? = .2 * w      .2/1.2
         const prevWidth = this.sidebarWidth();
-        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
-        this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+        this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+        this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
     sidebarDown = (e: React.PointerEvent) => {
@@ -473,8 +473,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
-        this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+        this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
     };
@@ -545,7 +545,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     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._scrollTop));
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
     infoWidth = () => this.props.PanelWidth() / 5;
@@ -566,7 +566,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     onPointerDown={async e => {
                         e.button === 0 && !e.ctrlKey && e.stopPropagation();
                     }}
-                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+                    style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
@@ -616,7 +616,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="MapBox2-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 7b437c7de..bc57148dc 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -32,7 +32,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
 
     addNoteClick = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
-            const newBox = Docs.Create.TextDocument('Note', { _autoHeight: true });
+            const newBox = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
             FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
             Doc.AddDocToList(this.props.place, 'data', newBox);
             this._stack?.scrollToBottom();
@@ -42,7 +42,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
     };
 
     _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
-    childFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
+    childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
     addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
     removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
     render() {
@@ -69,7 +69,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                             rootSelected={returnFalse}
                             childHideResizeHandles={returnTrue}
                             childHideDecorationTitle={returnTrue}
-                            childFitWidth={this.childFitWidth}
+                            childLayoutFitWidth={this.childLayoutFitWidth}
                             // childDocumentsActive={returnFalse}
                             removeDocument={this.removeDoc}
                             addDocument={this.addDoc}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6aa04e356..edeaba322 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -63,7 +63,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         super(props);
         const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
         const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
-        !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        !this.Document._layout_fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
         if (this.pdfUrl) {
             if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => (this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)));
             else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf)));
@@ -122,15 +122,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
-        croppingProto.isPrototype = true;
+        croppingProto.isDataDoc = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
         croppingProto.layout = ImageBox.LayoutString('data');
         croppingProto.data = new ImageField(Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png'));
-        croppingProto['data-nativeWidth'] = anchw;
-        croppingProto['data-nativeHeight'] = anchh;
+        croppingProto['data_nativeWidth'] = anchw;
+        croppingProto['data_nativeHeight'] = anchh;
         if (addCrop) {
-            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
+            DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
         }
         this.props.bringToFront(cropping);
 
@@ -140,8 +140,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             htmlString,
             anchw,
             anchh,
-            (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
-            (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
+            (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']),
+            (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']),
             4
         )
             .then((data_url: any) => {
@@ -173,15 +173,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.layoutDoc[HeightSym](),
                 this.props.PanelWidth(),
                 this.props.PanelHeight(),
-                NumCast(this.layoutDoc._scrollTop),
-                NumCast(this.rootDoc[this.fieldKey + '-nativeHeight'], 1),
+                NumCast(this.layoutDoc._layout_scrollTop),
+                NumCast(this.rootDoc[this.fieldKey + '_nativeHeight'], 1),
                 true,
                 this.layoutDoc[Id] + '-icon',
                 (iconFile: string, nativeWidth: number, nativeHeight: number) => {
                     setTimeout(() => {
                         this.dataDoc.icon = new ImageField(iconFile);
-                        this.dataDoc['icon-nativeWidth'] = nativeWidth;
-                        this.dataDoc['icon-nativeHeight'] = nativeHeight;
+                        this.dataDoc['icon_nativeWidth'] = nativeWidth;
+                        this.dataDoc['icon_nativeHeight'] = nativeHeight;
                     }, 500);
                 }
             );
@@ -201,13 +201,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(
-            () => this.rootDoc.scrollTop,
+            () => this.rootDoc.layout_scrollTop,
             () => {
-                if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '-panY'])) instanceof ComputedField)) {
-                    this.props.Document[this.SidebarKey + '-panY'] = ComputedField.MakeFunction('this.scrollTop');
+                if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
+                    this.props.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop');
                 }
-                this.props.Document[this.SidebarKey + '-viewScale'] = 1;
-                this.props.Document[this.SidebarKey + '-panX'] = 0;
+                this.props.Document[this.SidebarKey + '_freeform_scale'] = 1;
+                this.props.Document[this.SidebarKey + '_freeform_panX'] = 0;
             }
         );
     }
@@ -215,7 +215,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view);
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
-        if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(false);
             return true;
         }
@@ -239,7 +239,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         const docAnchor = () => {
             const anchor = Docs.Create.TextanchorDocument({
-                title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
+                title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)),
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
@@ -258,11 +258,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     loaded = (nw: number, nh: number, np: number) => {
-        this.dataDoc[this.props.fieldKey + '-numPages'] = np;
+        this.dataDoc[this.props.fieldKey + '_numPages'] = np;
         Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), (nw * 96) / 72));
         Doc.SetNativeHeight(this.dataDoc, (nh * 96) / 72);
         this.layoutDoc._height = this.layoutDoc[WidthSym]() / (Doc.NativeAspect(this.dataDoc) || 1);
-        !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        !this.Document._layout_fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
     };
 
     public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => {
@@ -279,14 +279,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public prevAnnotation = () => this._pdfViewer?.prevAnnotation();
     public nextAnnotation = () => this._pdfViewer?.nextAnnotation();
     public backPage = () => {
-        this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1);
+        this.Document._layout_curPage = Math.max(1, (NumCast(this.Document._layout_curPage) || 1) - 1);
         return true;
     };
     public forwardPage = () => {
-        this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '-numPages']), (NumCast(this.Document._curPage) || 1) + 1);
+        this.Document._layout_curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '_numPages']), (NumCast(this.Document._layout_curPage) || 1) + 1);
         return true;
     };
-    public gotoPage = (p: number) => (this.Document._curPage = p);
+    public gotoPage = (p: number) => (this.Document._layout_curPage = p);
 
     @undoBatch
     onKeyDown = action((e: KeyboardEvent) => {
@@ -316,9 +316,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
 
     // adding external documents; to sidebar key
-    // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"-annotation")
+    // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"_annotation")
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => {
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._show_sidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
     };
     sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => {
@@ -332,13 +332,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     .ScreenToLocalTransform()
                     .scale(this.props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
-                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
                 const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                    this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
                 }
                 return false;
             },
@@ -352,7 +352,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
@@ -363,7 +363,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         } else {
             this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
             this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
-            this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+            this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
         }
     });
     settingsPanel() {
@@ -378,7 +378,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </>
         );
         const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`;
-        const curPage = NumCast(this.Document._curPage) || 1;
+        const curPage = NumCast(this.Document._layout_curPage) || 1;
         return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
             <div
                 className="pdfBox-ui"
@@ -424,7 +424,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <input
                         value={curPage}
                         style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: 'all' }}
-                        onChange={e => (this.Document._curPage = Number(e.currentTarget.value))}
+                        onChange={e => (this.Document._layout_curPage = Number(e.currentTarget.value))}
                         onKeyDown={e => e.stopPropagation()}
                         onClick={action(() => (this._pageControls = !this._pageControls))}
                     />
@@ -470,7 +470,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._show_sidebar ? true : false;
     }
     @computed get sidebarHandle() {
         return (
@@ -480,7 +480,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={e => this.sidebarBtnDown(e, true)}>
@@ -490,10 +490,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     @computed get pdfScale() {
-        const pdfNativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+        const pdfNativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth, pdfNativeWidth);
         const pdfRatio = pdfNativeWidth / nativeWidth;
         return (pdfRatio * this.props.PanelWidth()) / pdfNativeWidth;
@@ -572,7 +572,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 onContextMenu={this.specificContextMenu}
                 style={{
                     display: this.props.thumbShown?.() ? 'none' : undefined,
-                    height: this.props.Document._scrollTop && !this.Document._fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
+                    height: this.props.Document._layout_scrollTop && !this.Document._layout_fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
                 }}>
                 <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
                 <div
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index f406ffbea..80b12b96e 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -41,7 +41,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
     setResult = (info: Upload.AccessPathInfo, presentation?: Presentation) => {
         this.result = info;
         this.dataDoc.type = DocumentType.VID;
-        this.dataDoc[this.fieldKey + '-duration'] = this.videoDuration;
+        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);
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 75e61bbeb..7bf765042 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -129,7 +129,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         }
     }
     getAnchor = (addAsAnnotation: boolean) => {
-        const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
+        const startTime = Cast(this.layoutDoc._layout_currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
         return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
     };
 
@@ -236,13 +236,13 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                 console.log('screenshotbox: upload');
                 const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
                 const [{ result }] = await Networking.UploadFilesToServer(file);
-                this.dataDoc[this.fieldKey + '-duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
+                this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
                 if (!(result instanceof Error)) {
                     // convert this screenshotBox into normal videoBox
                     this.dataDoc.type = DocumentType.VID;
                     this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
                     this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
-                    this.layoutDoc._fitWidth = undefined;
+                    this.layoutDoc._layout_fitWidth = undefined;
                     this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
                 } else alert('video conversion failed');
             };
@@ -270,14 +270,14 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     setupDictation = () => {
         if (this.dataDoc[this.fieldKey + '-dictation']) return;
         const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-        dictationText._autoHeight = false;
+        dictationText._layout_autoHeight = false;
         const dictationTextProto = Doc.GetProto(dictationText);
         dictationTextProto.recordingSource = this.dataDoc;
         dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
         dictationTextProto.mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState');
         this.dataDoc[this.fieldKey + '-dictation'] = dictationText;
     };
-    videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth();
+    videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth();
     formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
     render() {
         TraceMobx();
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index f09962b22..37fda14fc 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -167,7 +167,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
     // only included in buttons, transforms scripting UI to a button
     @action
     onFinish = () => {
-        this.rootDoc.layoutKey = 'layout';
+        this.rootDoc.layout_fieldKey = 'layout';
     };
 
     // displays error message
@@ -702,7 +702,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
     // toolbar (with compile and apply buttons) for scripting UI
     renderScriptingTools() {
-        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? '-finish' : '');
+        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
         return (
             <div className="scriptingBox-toolbar">
                 <button
@@ -730,7 +730,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
                     Save
                 </button>
 
-                {!StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout
+                {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout
                     <button
                         className={buttonStyle}
                         onPointerDown={e => {
@@ -776,7 +776,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
     // toolbar (with edit and run buttons and error message) for params UI
     renderTools(toolSet: string, func: () => void) {
-        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? '-finish' : '');
+        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
         return (
             <div className="scriptingBox-toolbar">
                 <button
@@ -795,7 +795,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
                     }}>
                     {toolSet}
                 </button>
-                {!StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? null : (
+                {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : (
                     <button
                         className={buttonStyle}
                         onPointerDown={e => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 7a7d4fe37..e00cb8618 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import { basename } from 'path';
-import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { Doc, HeightSym, StrListCast, WidthSym } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
@@ -91,9 +91,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return LinkManager.Links(this.dataDoc);
     }
     @computed get heightPercent() {
-        return NumCast(this.layoutDoc._timelineHeightPercent, 100);
+        return NumCast(this.layoutDoc._layout_timelineHeightPercent, 100);
     } // current percent of video relative to VideoBox height
-    // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
+    // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "_duration"]); }
     @observable rawDuration: number = 0;
 
     @computed get youtubeVideoId() {
@@ -325,14 +325,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 y: NumCast(this.layoutDoc.y, 1),
                 _width: 150,
                 _height: 50,
-                title: (this.layoutDoc._currentTimecode || 0).toString(),
+                title: (this.layoutDoc._layout_currentTimecode || 0).toString(),
                 onClick: FollowLinkScript(),
             });
             this.props.addDocument?.(b);
-            DocUtils.MakeLink(b, this.rootDoc, { linkRelationship: 'video snapshot' });
+            DocUtils.MakeLink(b, this.rootDoc, { link_relationship: 'video snapshot' });
             Networking.PostToServer('/youtubeScreenshot', {
                 id: this.youtubeVideoId,
-                timecode: this.layoutDoc._currentTimecode,
+                timecode: this.layoutDoc._layout_currentTimecode,
             }).then(response => {
                 const resolved = response?.accessPaths?.agnostic?.client;
                 if (resolved) {
@@ -345,7 +345,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
             // if you want to preview the captured image,
             const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
-            const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, '_'));
+            const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._layout_currentTimecode || 0).toString().replace(/\./, '_'));
             const filename = basename(encodedFilename);
             Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY));
         }
@@ -354,8 +354,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     updateIcon = () => {
         const makeIcon = (returnedfilename: string) => {
             this.dataDoc.icon = new ImageField(returnedfilename);
-            this.dataDoc['icon-nativeWidth'] = this.layoutDoc[WidthSym]();
-            this.dataDoc['icon-nativeHeight'] = this.layoutDoc[HeightSym]();
+            this.dataDoc.icon_nativeWidth = this.layoutDoc[WidthSym]();
+            this.dataDoc.icon_nativeHeight = this.layoutDoc[HeightSym]();
         };
         this.Snapshot(undefined, undefined, makeIcon);
     };
@@ -373,18 +373,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             onClick: FollowLinkScript(),
             _width: 150,
             _height: (height / width) * 150,
-            title: '--snapshot' + NumCast(this.layoutDoc._currentTimecode) + ' image-',
+            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));
         this.props.addDocument?.(imageSnapshot);
-        const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { linkRelationship: 'video snapshot' });
-        link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
+        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);
         setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true));
     };
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
-        const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
+        const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
         const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
         if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
         const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc;
@@ -402,16 +402,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         }
         if (Number.isFinite(this.player!.duration)) {
             this.rawDuration = this.player!.duration;
-            this.dataDoc[this.fieldKey + '-duration'] = this.rawDuration;
-        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '-duration']);
+            this.dataDoc[this.fieldKey + '_duration'] = this.rawDuration;
+        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '_duration']);
     });
 
     // updates video time
     @action
     updateTimecode = () => {
-        this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
+        this.player && (this.layoutDoc._layout_currentTimecode = this.player.currentTime);
         try {
-            this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
+            this._youtubePlayer && (this.layoutDoc._layout_currentTimecode = this._youtubePlayer.getCurrentTime?.());
         } catch (e) {
             console.log('Video Timecode Exception:', e);
         }
@@ -423,8 +423,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // extracts video thumbnails and saves them as field of doc
     getVideoThumbnails = () => {
-        if (this.dataDoc.thumbnails !== undefined) return;
-        this.dataDoc.thumbnails = new List<string>();
+        if (this.dataDoc[this.fieldKey + '_thumbnails'] !== undefined) return;
+        this.dataDoc[this.fieldKey + '_thumbnails'] = new List<string>();
         const thumbnailPromises: Promise<any>[] = [];
         const video = document.createElement('video');
 
@@ -442,7 +442,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             if (newTime < video.duration) {
                 video.currentTime = newTime;
             } else {
-                Promise.all(thumbnailPromises).then(thumbnails => (this.dataDoc.thumbnails = new List<string>(thumbnails)));
+                Promise.all(thumbnailPromises).then(thumbnails => (this.dataDoc[this.fieldKey + '_thumbnails'] = new List<string>(thumbnails)));
             }
         };
 
@@ -460,12 +460,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
             this._disposers.reactionDisposer?.();
             this._disposers.reactionDisposer = reaction(
-                () => NumCast(this.layoutDoc._currentTimecode),
+                () => NumCast(this.layoutDoc._layout_currentTimecode),
                 time => !this._playing && (vref.currentTime = time),
                 { fireImmediately: true }
             );
 
-            (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
+            (!this.dataDoc[this.fieldKey + '_thumbnails'] || this.dataDoc[this.fieldKey + '_thumbnails'].length != VideoBox.numThumbnails) && this.getVideoThumbnails();
         }
     };
 
@@ -531,7 +531,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
                         // delete assoicated video data
                         this.dataDoc[this.props.fieldKey] = '';
-                        this.dataDoc[this.fieldKey + '-duration'] = '';
+                        this.dataDoc[this.fieldKey + '_duration'] = '';
                         // delete assoicated presentation data
                         this.dataDoc[this.fieldKey + '-presentation'] = '';
                     },
@@ -573,7 +573,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         key="video"
                         autoPlay={this._screenCapture}
                         ref={this.setVideoRef}
-                        style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.rootDoc._viewScale)})`, transformOrigin: 'top left' } : {}}
+                        style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.rootDoc._freeform_scale)})`, transformOrigin: 'top left' } : {}}
                         onCanPlay={this.videoLoad}
                         controls={VideoBox._nativeControls}
                         onPlay={() => this.Play()}
@@ -620,8 +620,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._disposers.reactionDisposer?.();
             this._disposers.youtubeReactionDisposer?.();
             this._disposers.reactionDisposer = reaction(
-                () => this.layoutDoc._currentTimecode,
-                () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode))
+                () => this.layoutDoc._layout_currentTimecode,
+                () => !this._playing && this.Seek(NumCast(this.layoutDoc._layout_currentTimecode))
             );
             this._disposers.youtubeReactionDisposer = reaction(
                 () => Doc.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
@@ -677,15 +677,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this._clicking = false;
                 if (this.props.isContentActive()) {
                     // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
-                    // this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
+                    // this.layoutDoc._layout_timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
 
-                    this.layoutDoc._timelineHeightPercent = 80;
+                    this.layoutDoc._layout_timelineHeightPercent = 80;
                 }
                 return false;
             }),
             emptyFunction,
             () => {
-                this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
+                this.layoutDoc._layout_timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
                 setTimeout(
                     action(() => (this._clicking = false)),
                     500
@@ -721,7 +721,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
         const classname = 'videoBox-content-YouTube' + (this._fullScreen ? '-fullScreen' : '');
-        const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
+        const start = untracked(() => Math.round(NumCast(this.layoutDoc._layout_currentTimecode)));
         return (
             <iframe
                 key={this._youtubeIframeId}
@@ -740,7 +740,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @action.bound
     addDocWithTimecode(doc: Doc | Doc[]): boolean {
         const docs = doc instanceof Doc ? [doc] : doc;
-        const curTime = NumCast(this.layoutDoc._currentTimecode);
+        const curTime = NumCast(this.layoutDoc._layout_currentTimecode);
         docs.forEach(doc => (doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1));
         return this.addDocument(doc);
     }
@@ -859,7 +859,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // starts marquee selection
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._viewScale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -890,7 +890,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             .scale(this.scaling())
             .translate(0, (-this.heightPercent / 100) * this.props.PanelHeight());
 
-    setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._currentTimecode = time);
+    setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._layout_currentTimecode = time);
 
     timelineHeight = () => (this.props.PanelHeight() * (100 - this.heightPercent)) / 100;
 
@@ -899,7 +899,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     scaling = () => this.props.NativeDimScaling?.() || 1;
 
     panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100;
-    panelHeight = () => (this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
+    panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
 
     screenToLocalTransform = () => {
         const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
@@ -956,9 +956,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 <CollectionStackedTimeline
                     ref={action((r: any) => (this._stackedTimeline = r))}
                     {...this.props}
+                    dataFieldKey={this.fieldKey}
                     fieldKey={this.annotationKey}
                     dictationKey={this.fieldKey + '-dictation'}
                     mediaPath={this.audiopath}
+                    thumbnails={() => StrListCast(this.dataDoc[this.fieldKey + '_thumbnails'])}
                     renderDepth={this.props.renderDepth + 1}
                     startTag={'_timecodeToShow' /* videoStart */}
                     endTag={'_timecodeToHide' /* videoEnd */}
@@ -1003,7 +1005,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const anchy = NumCast(cropping.y);
         const anchw = NumCast(cropping._width);
         const anchh = NumCast(cropping._height);
-        const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw;
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
@@ -1014,25 +1016,25 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
-        croppingProto.isPrototype = true;
+        croppingProto.isDataDoc = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.VID;
         croppingProto.layout = VideoBox.LayoutString('data');
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
-        croppingProto['data-nativeWidth'] = anchw;
-        croppingProto['data-nativeHeight'] = anchh;
+        croppingProto['data_nativeWidth'] = anchw;
+        croppingProto['data_nativeHeight'] = anchh;
         croppingProto.videoCrop = true;
-        croppingProto.currentTimecode = this.layoutDoc._currentTimecode;
-        croppingProto.viewScale = viewScale;
-        croppingProto.viewScaleMin = viewScale;
-        croppingProto.panX = anchx / viewScale;
-        croppingProto.panY = anchy / viewScale;
-        croppingProto.panXMin = anchx / viewScale;
-        croppingProto.panXMax = anchw / viewScale;
-        croppingProto.panYMin = anchy / viewScale;
-        croppingProto.panYMax = anchh / viewScale;
+        croppingProto.layout_currentTimecode = this.layoutDoc._layout_currentTimecode;
+        croppingProto.freeform_scale = viewScale;
+        croppingProto.freeform_scaleMin = viewScale;
+        croppingProto.freeform_ = anchx / viewScale;
+        croppingProto.freeform_panY = anchy / viewScale;
+        croppingProto.freeform_panXMin = anchx / viewScale;
+        croppingProto.freeform_panXMax = anchw / viewScale;
+        croppingProto.freeform_panYMin = anchy / viewScale;
+        croppingProto.freeform_panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
+            DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
         }
         this.props.bringToFront(cropping);
         return cropping;
@@ -1050,7 +1052,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 style={{
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
                     borderRadius,
-                    overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? 'auto' : undefined,
+                    overflow: this.props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined,
                 }}
                 onWheel={e => {
                     e.stopPropagation();
@@ -1116,7 +1118,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @computed get UIButtons() {
         const bounds = this.props.docViewPath().lastElement().getBounds();
         const width = (bounds?.right || 0) - (bounds?.left || 0);
-        const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
+        const curTime = NumCast(this.layoutDoc._layout_currentTimecode) - (this.timeline?.clipStart || 0);
         return (
             <>
                 <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 82c8e796d..3da5d8f17 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -56,7 +56,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _keyInput = React.createRef<HTMLInputElement>();
-    private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop));
+    private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.layout_scrollTop));
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
     private _searchString = '';
@@ -101,7 +101,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             ImageCast(
                 this.layoutDoc['thumb-frozen'],
                 ImageCast(
-                    this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) && this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight)
+                    this.layoutDoc.thumbScrollTop === this.layoutDoc._layout_scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) && this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight)
                         ? this.layoutDoc.thumb
                         : undefined
                 )
@@ -144,7 +144,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     updateThumb = async () => {
         if (!this._iframe) return;
-        const scrollTop = NumCast(this.layoutDoc._scrollTop);
+        const scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
         const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
         var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
@@ -181,12 +181,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
-            this._annotationKeySuffix = () => this._urlHash + '-annotations';
+            this._annotationKeySuffix = () => this._urlHash + '_annotations';
             const reqdFuncs: { [key: string]: string } = {};
-            // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
-            reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"])`;
-            reqdFuncs[this.fieldKey + '-annotations-setter'] = `this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"] = value`;
-            reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"])`;
+            // bcz: need to make sure that doc.data_annotations points to the currently active web page's annotations (this could/should be when the doc is created)
+            reqdFuncs[this.fieldKey + '_annotations'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"])`;
+            reqdFuncs[this.fieldKey + '_annotations-setter'] = `this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"] = value`;
+            reqdFuncs[this.fieldKey + '_sidebar'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_sidebar"])`;
             DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
         this._disposers.urlchange = reaction(
@@ -196,12 +196,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         );
 
-        this._disposers.autoHeight = reaction(
-            () => this.layoutDoc._autoHeight,
-            autoHeight => {
-                if (autoHeight) {
-                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
+        this._disposers.layout_autoHeight = reaction(
+            () => this.layoutDoc._layout_autoHeight,
+            layout_autoHeight => {
+                if (layout_autoHeight) {
+                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -225,7 +225,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
 
         this._disposers.scrollReaction = reaction(
-            () => NumCast(this.layoutDoc._scrollTop),
+            () => NumCast(this.layoutDoc._layout_scrollTop),
             scrollTop => {
                 const viewTrans = StrCast(this.Document._viewTransition);
                 const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
@@ -281,7 +281,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     focus = (anchor: Doc, options: DocFocusOptions) => {
         if (anchor !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight));
+            const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight));
             if (scrollTo !== undefined) {
                 if (this._initialScroll === undefined) {
                     const focusTime = options.zoomTime ?? 500;
@@ -296,14 +296,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     getView = (doc: Doc) => {
-        if (this.rootDoc.layoutKey === 'layout_icon') this.props.DocumentView?.().iconify();
+        if (this.rootDoc.layout_fieldKey === 'layout_icon') this.props.DocumentView?.().iconify();
         if (this._url && WebCast(doc.presData).url.href !== this._url) this.setData(WebCast(doc.presData).url.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
     };
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
-        if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(false);
             return true;
         }
@@ -321,8 +321,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchor =
             this._getAnchor(this._savedAnnotations, false) ??
             Docs.Create.WebanchorDocument({
-                title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
-                y: NumCast(this.layoutDoc._scrollTop),
+                title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._layout_scrollTop),
+                y: NumCast(this.layoutDoc._layout_scrollTop),
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
@@ -349,9 +349,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._selectionText = sel.toString();
                 AnchorMenu.Instance.setSelectedText(sel.toString());
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
-                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
+                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
                 // Changing which document to add the annotation to (the currently selected WebBox)
-                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}-${this._urlHash}-sidebar`);
+                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash}_sidebar`);
                 GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             }
         }
@@ -364,7 +364,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-        e.button !== 2 && (this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale]);
+        e.button !== 2 && (this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale]);
         if (word || ((e.target as any) || '').className.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
             setTimeout(
                 action(() => (this._marqueeing = undefined)),
@@ -485,7 +485,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 batch.end();
                             });
                             if (this._outerRef.current) {
-                                this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
+                                this._outerRef.current.scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
                                 this._outerRef.current.scrollLeft = 0;
                             }
                         }
@@ -508,8 +508,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (e.ctrlKey) {
             if (this._innerCollectionView) {
                 this._innerCollectionView.zoom(e.screenX, e.screenY, e.deltaY);
-                const offset = e.clientY - NumCast(this.layoutDoc._scrollTop);
-                this.layoutDoc.panY = offset - offset / NumCast(this.layoutDoc._viewScale) + NumCast(this.layoutDoc._scrollTop) - NumCast(this.layoutDoc._scrollTop) / NumCast(this.layoutDoc._viewScale);
+                const offset = e.clientY - NumCast(this.layoutDoc._layout_scrollTop);
+                this.layoutDoc.panY = offset - offset / NumCast(this.layoutDoc._freeform_scale) + NumCast(this.layoutDoc._layout_scrollTop) - NumCast(this.layoutDoc._layout_scrollTop) / NumCast(this.layoutDoc._freeform_scale);
             }
             e.preventDefault();
         }
@@ -532,7 +532,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.layoutDoc.thumbScrollTop = undefined;
                 this.layoutDoc.thumbNativeWidth = undefined;
                 this.layoutDoc.thumbNativeHeight = undefined;
-                this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
+                this.layoutDoc.layout_scrollTop = this._outerRef.current.scrollTop = newScrollTop;
             } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
         }, timeout);
     };
@@ -550,13 +550,13 @@ 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.rootDoc[this.fieldKey + '_future'], listSpec('string'), []);
+        const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
         if (checkAvailable) return future.length;
         runInAction(() => {
             if (future.length) {
                 const curUrl = this._url;
-                this.dataDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
+                this.dataDoc[this.fieldKey + '_history'] = new List<string>([...history, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
                 this._scrollHeight = 0;
                 if (this._webUrl === this._url) {
@@ -572,14 +572,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     back = (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.rootDoc[this.fieldKey + '_future'], listSpec('string'));
+        const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
         if (checkAvailable) return history.length;
         runInAction(() => {
             if (history.length) {
                 const curUrl = this._url;
-                if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
-                else this.dataDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
+                if (future === undefined) this.dataDoc[this.fieldKey + '_future'] = new List<string>([this._url]);
+                else this.dataDoc[this.fieldKey + '_future'] = new List<string>([...future, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
                 this._scrollHeight = 0;
                 if (this._webUrl === this._url) {
@@ -643,10 +643,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (Field.toString(data) === this._url) return false;
         this._scrollHeight = 0;
         const oldUrl = this._url;
-        const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []);
+        const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
         const weburl = new WebField(Field.toString(data));
-        this.dataDoc[this.fieldKey + '-future'] = new List<string>([]);
-        this.dataDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), oldUrl]);
+        this.dataDoc[this.fieldKey + '_future'] = new List<string>([]);
+        this.dataDoc[this.fieldKey + '_history'] = new List<string>([...(history || []), oldUrl]);
         this.dataDoc[this.fieldKey] = weburl;
         return true;
     };
@@ -659,7 +659,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
         if (!cm.findByDescription('Options...')) {
-            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : 'Use') + ' Cors', event: () => (this.layoutDoc.useCors = !this.layoutDoc.useCors), icon: 'snowflake' });
+            !Doc.noviceMode &&
+                funcs.push({ description: (this.layoutDoc[this.fieldKey + '_useCors'] ? "Don't Use" : 'Use') + ' Cors', event: () => (this.layoutDoc[this.fieldKey + '_useCors'] = !this.layoutDoc[this.fieldKey + '_useCors']), icon: 'snowflake' });
             funcs.push({
                 description: (this.layoutDoc.allowScripts ? 'Prevent' : 'Allow') + ' Scripts',
                 event: () => {
@@ -672,12 +673,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 icon: 'snowflake',
             });
             funcs.push({
-                description: (!this.layoutDoc.forceReflow ? 'Force' : 'Prevent') + ' Reflow',
+                description: (!this.layoutDoc.layout_forceReflow ? 'Force' : 'Prevent') + ' Reflow',
                 event: () => {
-                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
-                    this.layoutDoc.forceReflow = !nw;
+                    const nw = !this.layoutDoc.layout_forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
+                    this.layoutDoc.layout_forceReflow = !nw;
                     if (nw) {
-                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + '-nativeWidth', nw, true);
+                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + '_nativeWidth', nw, true);
                     }
                 },
                 icon: 'snowflake',
@@ -726,7 +727,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setTimeout(
             action(() => {
                 if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
-                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.layout_scrollTop)));
                 }
                 this._webPageHasBeenRendered = true;
             })
@@ -736,7 +737,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             return <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
         }
         if (field instanceof WebField) {
-            const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl;
+            const url = this.layoutDoc[this.fieldKey + '_useCors'] ? Utils.CorsProxy(this._webUrl) : this._webUrl;
             return (
                 <iframe
                     className="webBox-iframe"
@@ -758,7 +759,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         return this.addDocumentWrapper(doc, sidebarKey);
     };
     @observable _draggingSidebar = false;
@@ -774,15 +775,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     .ScreenToLocalTransform()
                     .scale(this.props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
-                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
-                const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight']);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
+                const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
                 const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
                     onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                    this.layoutDoc._layout_showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
                 }
                 return false;
             }),
@@ -804,7 +805,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.layoutDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={e => this.sidebarBtnDown(e, true)}>
@@ -815,12 +816,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         if (!nativeWidth) {
             const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym]();
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
             Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth);
-            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         }
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
@@ -830,10 +831,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
         } else {
-            this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar;
+            this.layoutDoc._layout_showSidebar = !this.layoutDoc._layout_showSidebar;
             this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
-            if (!this.layoutDoc._showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
-                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '-nativeWidth'] = undefined;
+            if (!this.layoutDoc._layout_showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
+                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '_nativeWidth'] = undefined;
             } else {
                 this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
             }
@@ -862,8 +863,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 className={'webBox-cont' + (interactive ? '-interactive' : '')}
                 onKeyDown={e => e.stopPropagation()}
                 style={{
-                    width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%',
-                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.panX)}px, ${-NumCast(this.layoutDoc.panY)}px)`,
+                    width: !this.layoutDoc.layout_forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']) || `100%` : '100%',
+                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`,
                 }}>
                 {this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc) ? null : this.urlContent}
             </div>
@@ -876,7 +877,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             <div
                 className="webBox-annotationLayer"
                 style={{
-                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.panX)}px, ${-NumCast(this.layoutDoc.panY)}px)`,
+                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`,
                     height: Doc.NativeHeight(this.Document) || undefined,
                 }}
                 ref={this._annotationLayer}>
@@ -889,7 +890,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     }
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     @computed get webpage() {
@@ -914,7 +915,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 ScreenToLocalTransform={this.scrollXf}
                 NativeDimScaling={returnOne}
                 focus={this.focus}
-                dropAction={'alias'}
+                dropAction="embed"
                 docFilters={docFilters}
                 select={emptyFunction}
                 isAnyChildContentActive={returnFalse}
@@ -989,7 +990,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
@@ -1055,7 +1056,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         ref={this._sidebarRef}
                         {...this.props}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                        fieldKey={this.fieldKey + '-' + this._urlHash}
+                        fieldKey={this.fieldKey + '_' + this._urlHash}
                         rootDoc={this.rootDoc}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index a4968dcd0..5e615f2c1 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -44,16 +44,12 @@ export enum ButtonType {
     ToggleButton = 'tglBtn',
     ColorButton = 'colorBtn',
     ToolButton = 'toolBtn',
-    NumberButton = 'numBtn',
+    NumberSliderButton = 'numSliderBtn',
+    NumberDropdownButton = 'numDropdownBtn',
+    NumberInlineButton = 'numInlineBtn',
     EditableText = 'editableText',
 }
 
-export enum NumButtonType {
-    Slider = 'slider',
-    DropdownOptions = 'list',
-    Inline = 'inline',
-}
-
 export interface ButtonProps extends FieldViewProps {
     type?: ButtonType;
 }
@@ -132,8 +128,52 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     /**
      * Number button
      */
-    @computed get numberButton() {
-        const numBtnType: string = StrCast(this.rootDoc.numBtnType);
+    @computed get numberSliderButton() {
+        const numScript = ScriptCast(this.rootDoc.script);
+        const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value');
+
+        // Script for checking the outcome of the toggle
+        const checkResult = Number(numScript?.script.run({ self: this.rootDoc, value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
+
+        const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
+
+        const dropdown = (
+            <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}>
+                <input
+                    type="range"
+                    step="1"
+                    min={NumCast(this.rootDoc.numBtnMin, 0)}
+                    max={NumCast(this.rootDoc.numBtnMax, 100)}
+                    value={checkResult}
+                    className="menu-slider"
+                    onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
+                    onPointerUp={() => this._batch?.end()}
+                    onChange={e => {
+                        e.stopPropagation();
+                        setValue(Number(e.target.value));
+                    }}
+                />
+            </div>
+        );
+        return (
+            <div
+                className="menuButton numBtn slider"
+                onPointerDown={e => e.stopPropagation()}
+                onClick={action(() => {
+                    this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
+                    this.noTooltip = this.rootDoc.dropDownOpen;
+                    Doc.UnBrushAllDocs();
+                })}>
+                {checkResult}
+                {label}
+                {this.rootDoc.dropDownOpen ? dropdown : null}
+            </div>
+        );
+    }
+    /**
+     * Number button
+     */
+    @computed get numberDropdownButton() {
         const numScript = ScriptCast(this.rootDoc.script);
         const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value');
 
@@ -142,102 +182,71 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
 
-        if (numBtnType === NumButtonType.Slider) {
-            const dropdown = (
-                <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}>
-                    <input
-                        type="range"
-                        step="1"
-                        min={NumCast(this.rootDoc.numBtnMin, 0)}
-                        max={NumCast(this.rootDoc.numBtnMax, 100)}
-                        value={checkResult}
-                        className="menu-slider"
-                        onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
-                        onPointerUp={() => this._batch?.end()}
-                        onChange={e => {
-                            e.stopPropagation();
-                            setValue(Number(e.target.value));
-                        }}
-                    />
+        const items: number[] = [];
+        for (let i = 0; i < 100; i++) {
+            if (i % 2 === 0) {
+                items.push(i);
+            }
+        }
+        const list = items.map(value => {
+            return (
+                <div
+                    className="list-item"
+                    key={`${value}`}
+                    style={{
+                        backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined,
+                    }}
+                    onClick={() => setValue(value)}>
+                    {value}
                 </div>
             );
-            return (
+        });
+        return (
+            <div className="menuButton numBtn list">
+                <div className="button" onClick={action(e => setValue(Number(checkResult) - 1))}>
+                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'minus'} />
+                </div>
                 <div
-                    className={`menuButton ${this.type} ${numBtnType}`}
-                    onPointerDown={e => e.stopPropagation()}
+                    className={`button ${'number'}`}
+                    onPointerDown={e => {
+                        e.stopPropagation();
+                        e.preventDefault();
+                    }}
                     onClick={action(() => {
                         this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
                         this.noTooltip = this.rootDoc.dropDownOpen;
                         Doc.UnBrushAllDocs();
                     })}>
-                    {checkResult}
-                    {label}
-                    {this.rootDoc.dropDownOpen ? dropdown : null}
+                    <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={undoBatch(action(e => setValue(Number(e.target.value))))} />
+                </div>
+                <div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}>
+                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
                 </div>
-            );
-        } else if (numBtnType === NumButtonType.DropdownOptions) {
-            const items: number[] = [];
-            for (let i = 0; i < 100; i++) {
-                if (i % 2 === 0) {
-                    items.push(i);
-                }
-            }
-            const list = items.map(value => {
-                return (
-                    <div
-                        className="list-item"
-                        key={`${value}`}
-                        style={{
-                            backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined,
-                        }}
-                        onClick={() => setValue(value)}>
-                        {value}
-                    </div>
-                );
-            });
-            return (
-                <div className={`menuButton ${this.type} ${numBtnType}`}>
-                    <div className={`button`} onClick={action(e => setValue(Number(checkResult) - 1))}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'minus'} />
-                    </div>
-                    <div
-                        className={`button ${'number'}`}
-                        onPointerDown={e => {
-                            e.stopPropagation();
-                            e.preventDefault();
-                        }}
-                        onClick={action(() => {
-                            this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
-                            this.noTooltip = this.rootDoc.dropDownOpen;
-                            Doc.UnBrushAllDocs();
-                        })}>
-                        <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={undoBatch(action(e => setValue(Number(e.target.value))))} />
-                    </div>
-                    <div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
-                    </div>
 
-                    {this.rootDoc.dropDownOpen ? (
-                        <div>
-                            <div className="menuButton-dropdownList" style={{ left: '25%' }}>
-                                {list}
-                            </div>
-                            <div
-                                className="dropbox-background"
-                                onClick={action(e => {
-                                    e.stopPropagation();
-                                    this.rootDoc.dropDownOpen = false;
-                                    this.noTooltip = false;
-                                    Doc.UnBrushAllDocs();
-                                })}
-                            />
+                {this.rootDoc.dropDownOpen ? (
+                    <div>
+                        <div className="menuButton-dropdownList" style={{ left: '25%' }}>
+                            {list}
                         </div>
-                    ) : null}
-                </div>
-            );
-        } else {
-            return <div />;
-        }
+                        <div
+                            className="dropbox-background"
+                            onClick={action(e => {
+                                e.stopPropagation();
+                                this.rootDoc.dropDownOpen = false;
+                                this.noTooltip = false;
+                                Doc.UnBrushAllDocs();
+                            })}
+                        />
+                    </div>
+                ) : null}
+            </div>
+        );
+    }
+    /**
+     * Number button
+     */
+    @computed get numberInlineButton() {
+        return <div />;
     }
 
     /**
@@ -528,7 +537,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             case ButtonType.EditableText: return this.editableText;
             case ButtonType.DropdownList: button = this.dropdownListButton; break;
             case ButtonType.ColorButton: button = this.colorButton; break;
-            case ButtonType.NumberButton: button = this.numberButton; break;
+            case ButtonType.NumberDropdownButton: button = this.numberDropdownButton; break;
+            case ButtonType.NumberInlineButton: button = this.numberInlineButton; break;
+            case ButtonType.NumberSliderButton: button = this.numberSliderButton; break;
             case ButtonType.DropdownButton: button = this.dropdownButton; break;
             case ButtonType.ToggleButton: button = this.toggleButton; break;
             case ButtonType.TextButton: 
@@ -613,7 +624,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
     }
     Doc.SharingDoc().userColor = undefined;
     Doc.GetProto(Doc.SharingDoc()).userColor = color;
-    Doc.UserDoc().showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().showTitle, 'creationDate');
+    Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'creationDate');
 });
 
 // toggle: Set overlay status of selected document
@@ -626,29 +637,29 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
 });
 
-ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snapline' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
+ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     // prettier-ignore
-    const map: Map<'grid' | 'snapline' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+    const map: Map<'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
         ['grid', {
             undo: false,
-            checkResult: (doc:Doc) => doc._backgroundGridShow,
-            setDoc: (doc:Doc) => doc._backgroundGridShow = !doc._backgroundGridShow,
+            checkResult: (doc:Doc) => doc._freeform_backgroundGrid,
+            setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
         }],
-        ['snapline', {
+        ['snaplines', {
             undo: false,
-            checkResult: (doc:Doc) => doc.showSnapLines,
-            setDoc: (doc:Doc) => doc._showSnapLines = !doc._showSnapLines,
+            checkResult: (doc:Doc) => doc._freeform_snapLines,
+            setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines,
         }],
         ['viewAll', {
             undo: false,
-            checkResult: (doc:Doc) => doc._fitContentsToBox,
-            setDoc: (doc:Doc) => doc._fitContentsToBox = !doc._fitContentsToBox,
+            checkResult: (doc:Doc) => doc._freeform_fitContentsToBox,
+            setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
         }],
         ['clusters', {
             undo: false,
-            checkResult: (doc:Doc) => doc._useClusters,
-            setDoc: (doc:Doc) => doc._useClusters = !doc._useClusters,
+            checkResult: (doc:Doc) => doc._freeform_useClusters,
+            setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters,
         }],
         ['arrange', {
             undo: true,
@@ -850,8 +861,8 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
     // prettier-ignore
     const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
         ['inkMask', {
-            checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'),
-            setInk: (doc: Doc) => (doc.isInkMask = !doc.isInkMask),
+            checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.stroke_isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'),
+            setInk: (doc: Doc) => (doc.stroke_isInkMask = !doc.stroke_isInkMask),
             setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
         }],
         ['fillColor', {
@@ -860,8 +871,8 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
             setMode: () => SetActiveFillColor(StrCast(value)),
         }],
         [ 'strokeWidth', {
-            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.strokeWidth) : ActiveInkWidth()),
-            setInk: (doc: Doc) => (doc.strokeWidth = NumCast(value)),
+            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.stroke_width) : ActiveInkWidth()),
+            setInk: (doc: Doc) => (doc.stroke_width = NumCast(value)),
             setMode: () => SetActiveInkWidth(value.toString()),
         }],
         ['strokeColor', {
@@ -914,24 +925,24 @@ ScriptingGlobals.add(function webBack(checkResult?: boolean) {
 ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult && selected) {
-        const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
+        const result: boolean = NumCast(selected.schema_previewWidth) > 0;
         if (result) return Colors.MEDIUM_BLUE;
         else return 'transparent';
     } else if (selected) {
-        if (NumCast(selected.schemaPreviewWidth) > 0) {
-            selected.schemaPreviewWidth = 0;
+        if (NumCast(selected.schema_previewWidth) > 0) {
+            selected.schema_previewWidth = 0;
         } else {
-            selected.schemaPreviewWidth = 200;
+            selected.schema_previewWidth = 200;
         }
     }
 });
 ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult && selected) {
-        return NumCast(selected._singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
+        return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
     }
     if (selected) {
-        selected._singleLine = !selected._singleLine;
+        selected._schema_singleLine = !selected._schema_singleLine;
     }
 });
 
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index c05a30d1a..c929b7ff3 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -41,18 +41,33 @@ export class DashDocView {
 
         this.root = ReactDOM.createRoot(this.dom);
         this.root.render(
-            <DashDocViewInternal docId={node.attrs.docId} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />
+            <DashDocViewInternal
+                docId={node.attrs.docId}
+                embedding={node.attrs.embedding}
+                width={node.attrs.width}
+                height={node.attrs.height}
+                hidden={node.attrs.hidden}
+                fieldKey={node.attrs.fieldKey}
+                tbox={tbox}
+                view={view}
+                node={node}
+                getPos={getPos}
+            />
         );
     }
     destroy() {
-        setTimeout(this.root.unmount);
+        setTimeout(() => {
+            try {
+                this.root.unmount();
+            } catch {}
+        });
     }
     selectNode() {}
 }
 
 interface IDashDocViewInternal {
     docId: string;
-    alias: string;
+    embedding: string;
     tbox: FormattedTextBox;
     width: string;
     height: string;
@@ -69,25 +84,18 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
     _textBox: FormattedTextBox;
     @observable _dashDoc: Doc | undefined;
     @observable _finalLayout: any;
-    @observable _resolvedDataDoc: any;
     @observable _width: number = 0;
     @observable _height: number = 0;
 
     updateDoc = action((dashDoc: Doc) => {
         this._dashDoc = dashDoc;
-        this._finalLayout = this.props.docId ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc);
+        this._finalLayout = dashDoc;
 
-        if (this._finalLayout) {
-            if (!Doc.AreProtosEqual(this._finalLayout, dashDoc)) {
-                this._finalLayout.rootDocument = dashDoc.aliasOf;
-            }
-            this._resolvedDataDoc = Cast(this._finalLayout.resolvedDataDoc, Doc, null);
-        }
         if (this.props.width !== (this._dashDoc?._width ?? '') + 'px' || this.props.height !== (this._dashDoc?._height ?? '') + 'px') {
             try {
                 this._width = NumCast(this._dashDoc?._width);
                 this._height = NumCast(this._dashDoc?._height);
-                // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
+                // bcz: an exception will be thrown if two embeddings are open at the same time when a doc view comment is made
                 this.props.view.dispatch(
                     this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
                         ...this.props.node.attrs,
@@ -105,15 +113,15 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
         super(props);
         this._textBox = this.props.tbox;
 
-        DocServer.GetRefField(this.props.docId + this.props.alias).then(async dashDoc => {
+        DocServer.GetRefField(this.props.docId + this.props.embedding).then(async dashDoc => {
             if (!(dashDoc instanceof Doc)) {
-                this.props.alias &&
+                this.props.embedding &&
                     DocServer.GetRefField(this.props.docId).then(async dashDocBase => {
                         if (dashDocBase instanceof Doc) {
-                            const aliasedDoc = Doc.MakeAlias(dashDocBase, this.props.docId + this.props.alias);
-                            aliasedDoc.layoutKey = 'layout';
-                            this.props.fieldKey && DocUtils.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, this.props.fieldKey, undefined);
-                            this.updateDoc(aliasedDoc);
+                            const embedding = Doc.MakeEmbedding(dashDocBase, this.props.docId + this.props.embedding);
+                            embedding.layout_fieldKey = 'layout';
+                            this.props.fieldKey && DocUtils.makeCustomViewClicked(embedding, Docs.Create.StackingDocument, this.props.fieldKey, undefined);
+                            this.updateDoc(embedding);
                         }
                     });
             } else {
@@ -191,7 +199,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                 onWheel={e => e.preventDefault()}>
                 <DocumentView
                     Document={this._finalLayout}
-                    DataDoc={this._resolvedDataDoc}
                     addDocument={returnFalse}
                     rootSelected={returnFalse} //{this._textBox.props.isSelected}
                     removeDocument={this.removeDoc}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index a9e59ab1c..6c61f6709 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -143,16 +143,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement();
         if (container) {
-            const alias = Doc.MakeAlias(container.props.Document);
-            alias._viewType = CollectionViewType.Time;
-            let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
+            const embedding = Doc.MakeEmbedding(container.rootDoc);
+            embedding._viewType = CollectionViewType.Time;
+            const colHdrKey = '_' + container.LayoutFieldKey + '_columnHeaders';
+            let list = Cast(embedding[colHdrKey], listSpec(SchemaHeaderField));
             if (!list) {
-                alias._columnHeaders = list = new List<SchemaHeaderField>();
+                embedding[colHdrKey] = list = new List<SchemaHeaderField>();
             }
             list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
             list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
-            alias._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
-            this.props.tbox.props.addDocTab(alias, OpenWhere.addRight);
+            embedding._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
+            this.props.tbox.props.addDocTab(embedding, OpenWhere.addRight);
         }
     };
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 88adba1e7..023814a9d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -118,38 +118,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return this._editorView;
     }
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
     }
 
     @computed get noSidebar() {
-        return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._noSidebar;
+        return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._layout_noSidebar;
     }
-    @computed get sidebarWidthPercent() {
-        return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    @computed get layout_sidebarWidthPercent() {
+        return this._showSidebar ? '20%' : StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.fieldKey + '-backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
-    @computed get autoHeight() {
-        return (this.props.forceAutoHeight || this.layoutDoc._autoHeight) && !this.props.ignoreAutoHeight;
+    @computed get layout_autoHeight() {
+        return (this.props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this.props.ignoreAutoHeight;
     }
     @computed get textHeight() {
-        return NumCast(this.rootDoc[this.fieldKey + '-height']);
+        return NumCast(this.rootDoc[this.fieldKey + '_height']);
     }
     @computed get scrollHeight() {
-        return NumCast(this.rootDoc[this.fieldKey + '-scrollHeight']);
+        return NumCast(this.rootDoc[this.fieldKey + '_scrollHeight']);
     }
     @computed get sidebarHeight() {
-        return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '-height']);
+        return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '_height']);
     }
     @computed get titleHeight() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
     }
-    @computed get autoHeightMargins() {
-        return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins);
+    @computed get layout_autoHeightMargins() {
+        return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins);
     }
     @computed get _recording() {
         return this.dataDoc?.mediaState === 'recording';
@@ -213,8 +213,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     public RemoveLinkFromDoc(linkDoc?: Doc) {
         this.unhighlightSearchTerms();
         const state = this._editorView?.state;
-        const a1 = linkDoc?.anchor1 as Doc;
-        const a2 = linkDoc?.anchor2 as Doc;
+        const a1 = linkDoc?.link_anchor_1 as Doc;
+        const a2 = linkDoc?.link_anchor_2 as Doc;
         if (state && a1 && a2 && this._editorView) {
             this.removeDocument(a1);
             this.removeDocument(a2);
@@ -255,18 +255,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         AnchorMenu.Instance.Status = 'marquee';
 
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => {
-            !this.layoutDoc.showSidebar && this.toggleSidebar();
+            !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
             setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
         };
         AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
-            !this.layoutDoc.showSidebar && this.toggleSidebar();
+            !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
             const anchor = this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true, true);
             setTimeout(() => {
                 const target = this._sidebarRef.current?.anchorMenuClick(anchor);
                 if (target) {
                     anchor.followLinkAudio = true;
                     DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
-                    target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
+                    target.title = ComputedField.MakeFunction(`self["text_audioAnnotations_text"].lastElement()`);
                 }
             });
         };
@@ -323,7 +323,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
                     this._applyingChange = this.fieldKey;
                     const textChange = curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text;
-                    textChange && (dataDoc[this.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())));
+                    textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
                     if ((!curTemp && !curProto) || curText || json.includes('dash')) {
                         // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
                         if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
@@ -333,7 +333,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             } else {
                                 dataDoc[this.fieldKey] = new RichTextField(json, curText);
                             }
-                            dataDoc[this.fieldKey + '-noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+                            dataDoc[this.fieldKey + '_noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
                             textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
                             unchanged = false;
                         }
@@ -341,7 +341,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         // if we've deleted all the text in a note driven by a template, then restore the template data
                         dataDoc[this.fieldKey] = undefined;
                         this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data)));
-                        dataDoc[this.fieldKey + '-noTemplate'] = undefined; // mark the data field as not being split from any template it might have
+                        dataDoc[this.fieldKey + '_noTemplate'] = undefined; // mark the data field as not being split from any template it might have
                         ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
                         unchanged = false;
                     }
@@ -371,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         let linkAnchor;
         let link;
         LinkManager.Links(this.dataDoc).forEach((l, i) => {
-            const anchor = (l.anchor1 as Doc).annotationOn ? (l.anchor1 as Doc) : (l.anchor2 as Doc).annotationOn ? (l.anchor2 as Doc) : undefined;
+            const anchor = (l.link_anchor_1 as Doc).annotationOn ? (l.link_anchor_1 as Doc) : (l.link_anchor_2 as Doc).annotationOn ? (l.link_anchor_2 as Doc) : undefined;
             if (anchor && (anchor.annotationOn as Doc).mediaState === 'recording') {
                 linkTime = NumCast(anchor._timecodeToShow /* audioStart */);
                 linkAnchor = anchor;
@@ -407,7 +407,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     autoLink = () => {
         const newAutoLinks = new Set<Doc>();
-        const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.linkRelationship === LinkManager.AutoKeywords);
+        const oldAutoLinks = LinkManager.Links(this.props.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;
@@ -419,7 +419,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
         }
-        oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
+        oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
     };
 
     updateTitle = () => {
@@ -428,7 +428,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             !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
             (title.startsWith('-') || title.startsWith('@')) &&
             this._editorView &&
-            !this.dataDoc['title-custom'] &&
+            !this.dataDoc.title_custom &&
             (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === 'text')
         ) {
             let node = this._editorView.state.doc;
@@ -460,8 +460,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
                             alink =
                                 alink ??
-                                (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) ||
-                                    DocUtils.MakeLink(this.props.Document, target, { linkRelationship: LinkManager.AutoKeywords })!);
+                                (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)) ||
+                                    DocUtils.MakeLink(this.props.Document, target, { link_relationship: LinkManager.AutoKeywords })!);
                             newAutoLinks.add(alink);
                             const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
                             allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
@@ -528,7 +528,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             this.setupEditor(this.config, this.fieldKey);
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc);
         }
-        // if (this.autoHeight) this.tryUpdateScrollHeight();
+        // if (this.layout_autoHeight) this.tryUpdateScrollHeight();
     };
 
     @undoBatch
@@ -554,11 +554,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     docId: target[Id],
                     float: 'unset',
                 });
-                if (!['alias', 'copy'].includes((dragData.dropAction ?? '') as any)) {
+                if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) {
                     dragData.removeDocument?.(dragData.draggedDocuments[0]);
                 }
-                target._fitContentsToBox = true;
-                target.context = this.rootDoc;
+                target._freeform_fitContentsToBox = true;
+                target.embedContainer = this.rootDoc;
                 const view = this._editorView!;
                 view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
                 e.stopPropagation();
@@ -655,14 +655,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     @action
     toggleSidebar = (preview: boolean = false) => {
         const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
         if (preview) this._showSidebar = true;
-        else this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
+        else this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
 
         this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
     };
@@ -685,11 +685,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             .ScreenToLocalTransform()
             .scale(this.props.NativeDimScaling?.() || 1)
             .transformDirection(delta[0], delta[1]);
-        const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.sidebarWidthPercent.replace('%', ''))) / 100;
+        const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
         const width = this.layoutDoc[WidthSym]() + localDelta[0];
-        this.layoutDoc._sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
+        this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
         this.layoutDoc.width = width;
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
     };
@@ -757,7 +757,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             description: 'plain',
             event: undoBatch(() => {
                 Doc.setNativeView(this.rootDoc);
-                this.layoutDoc.autoHeightMargins = undefined;
+                this.layoutDoc.layout_autoHeightMargins = undefined;
             }),
             icon: 'eye',
         });
@@ -765,18 +765,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             description: 'metadata',
             event: undoBatch(() => {
                 this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
-                this.rootDoc.layoutKey = 'layout_meta';
-                setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50), 50);
+                this.rootDoc.layout_fieldKey = 'layout_meta';
+                setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._layout_autoHeightMargins = 50), 50);
             }),
             icon: 'eye',
         });
-        const noteTypesDoc = Cast(Doc.UserDoc()['template-notes'], Doc, null);
+        const noteTypesDoc = Cast(Doc.UserDoc().template_notes, Doc, null);
         DocListCast(noteTypesDoc?.data).forEach(note => {
             const icon: IconProp = StrCast(note.icon) as IconProp;
             changeItems.push({
                 description: StrCast(note.title),
                 event: undoBatch(() => {
-                    this.layoutDoc.autoHeightMargins = undefined;
+                    this.layoutDoc.layout_autoHeightMargins = undefined;
                     Doc.setNativeView(this.rootDoc);
                     DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
                 }),
@@ -802,11 +802,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         );
 
         const uicontrols: ContextMenuProps[] = [];
-        uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: !this.Document._noSidebar ? 'eye-slash' : 'eye' });
         uicontrols.push({
-            description: (this.Document._showAltContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
-            event: () => (this.layoutDoc._showAltContentUI = !this.layoutDoc._showAltContentUI),
-            icon: !this.Document._showAltContentUI ? 'eye-slash' : 'eye',
+            description: !this.Document._layout_noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle',
+            event: () => (this.layoutDoc._layout_noSidebar = !this.layoutDoc._layout_noSidebar),
+            icon: !this.Document._layout_noSidebar ? 'eye-slash' : 'eye',
+        });
+        uicontrols.push({
+            description: (this.Document._layout_altContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
+            event: () => (this.layoutDoc._layout_altContentUI = !this.layoutDoc._layout_altContentUI),
+            icon: !this.Document._layout_altContentUI ? 'eye-slash' : 'eye',
         });
         uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
         !Doc.noviceMode &&
@@ -837,10 +841,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
                         this.rootDoc.isTemplateDoc = false;
                         this.rootDoc.isTemplateForField = '';
-                        this.rootDoc.layoutKey = 'layout';
+                        this.rootDoc.layout_fieldKey = 'layout';
                         MakeTemplate(this.rootDoc, true, title);
                         setTimeout(() => {
-                            this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height
+                            this.rootDoc._layout_autoHeight = this.layoutDoc._layout_autoHeight; // layout_autoHeight, width and height
                             this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
                             this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root.  This should probably apply to all '_' fields
                             this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, 'string', null);
@@ -848,7 +852,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         }, 10);
                     }
                     Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
-                    Doc.AddDocToList(Cast(Doc.UserDoc()['template-notes'], Doc, null), 'data', this.rootDoc);
+                    Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.rootDoc);
                 },
                 icon: 'eye',
             });
@@ -859,11 +863,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
         optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
         optionItems.push({
-            description: !this.Document._singleLine ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
-            event: () => (this.layoutDoc._singleLine = !this.layoutDoc._singleLine),
-            icon: !this.Document._singleLine ? 'grip-lines' : 'bars',
+            description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
+            event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
+            icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
         });
-        !Doc.noviceMode && optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: this.Document._autoHeight ? 'lock' : 'unlock' });
+        !Doc.noviceMode &&
+            optionItems.push({
+                description: `${this.Document._layout_autoHeight ? 'Lock' : 'Auto'} Height`,
+                event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight),
+                icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
+            });
         optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' });
         !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
         this._downX = this._downY = Number.NaN;
@@ -903,8 +912,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     y: NumCast(this.rootDoc.y),
                     _height: 200,
                     _width: 200,
-                    'data-nativeWidth': result.nativeWidth,
-                    'data-nativeHeight': result.nativeHeight,
+                    data_nativeWidth: result.nativeWidth,
+                    data_nativeHeight: result.nativeHeight,
                 });
                 if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc)) {
                     newDoc.overlayX = this.rootDoc.x;
@@ -914,7 +923,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     this.props.addDocument?.(newDoc);
                 }
                 // Create link between prompt and image
-                DocUtils.MakeLink(this.rootDoc, newDoc, { linkRelationship: 'Image Prompt' });
+                DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
             }
         } catch (err) {
             console.log(err);
@@ -957,8 +966,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement();
                 if (link) {
                     Doc.GetProto(link).isDictation = true;
-                    const audioanchor = Cast(link.anchor2, Doc, null);
-                    const textanchor = Cast(link.anchor1, Doc, null);
+                    const audioanchor = Cast(link.link_anchor_2, Doc, null);
+                    const textanchor = Cast(link.link_anchor_1, Doc, null);
                     if (audioanchor) {
                         audioanchor.backgroundColor = 'tan';
                         const audiotag = this._editorView.state.schema.nodes.audiotag.create({
@@ -1082,11 +1091,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     };
 
-    // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
+    // if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
     // Since we also monitor all component height changes, this will update the document's height.
     resetNativeHeight = (scrollHeight: number) => {
         const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight);
-        this.rootDoc[this.fieldKey + '-height'] = scrollHeight;
+        this.rootDoc[this.fieldKey + '_height'] = scrollHeight;
         if (nh) this.layoutDoc._nativeHeight = scrollHeight;
     };
 
@@ -1097,9 +1106,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this._cachedLinks = LinkManager.Links(this.Document);
         this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation);
-        this._disposers.autoHeight = reaction(
-            () => this.autoHeight,
-            autoHeight => autoHeight && this.tryUpdateScrollHeight()
+        this._disposers.layout_autoHeight = reaction(
+            () => this.layout_autoHeight,
+            layout_autoHeight => layout_autoHeight && this.tryUpdateScrollHeight()
         );
         this._disposers.highlights = reaction(
             () => Array.from(FormattedTextBox._globalHighlights).slice(),
@@ -1111,16 +1120,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             width => this.tryUpdateScrollHeight()
         );
         this._disposers.scrollHeight = reaction(
-            () => ({ scrollHeight: this.scrollHeight, autoHeight: this.autoHeight, width: NumCast(this.layoutDoc._width) }),
-            ({ width, scrollHeight, autoHeight }) => width && autoHeight && this.resetNativeHeight(scrollHeight),
+            () => ({ scrollHeight: this.scrollHeight, layout_autoHeight: this.layout_autoHeight, width: NumCast(this.layoutDoc._width) }),
+            ({ width, scrollHeight, layout_autoHeight }) => width && layout_autoHeight && this.resetNativeHeight(scrollHeight),
             { fireImmediately: true }
         );
         this._disposers.componentHeights = reaction(
-            // set the document height when one of the component heights changes and autoHeight is on
-            () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }),
-            ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
+            // set the document height when one of the component heights changes and layout_autoHeight is on
+            () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layout_autoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }),
+            ({ sidebarHeight, textHeight, layout_autoHeight, marginsHeight }) => {
                 const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
-                if (autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) {
+                if (layout_autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) {
                     this.props.setHeight?.(newHeight);
                 }
             },
@@ -1145,7 +1154,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._disposers.editorState = reaction(
             () => {
                 const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc?.proto), this.fieldKey) ? DocCast(this.layoutDoc?.proto) : this?.dataDoc;
-                const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : dataDoc?.[this.fieldKey + '-noTemplate'] || !this.layoutDoc[this.fieldKey] ? dataDoc : this.layoutDoc;
+                const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : dataDoc?.[this.fieldKey + '_noTemplate'] || !this.layoutDoc[this.fieldKey] ? dataDoc : this.layoutDoc;
                 return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(whichDoc[this.fieldKey]) };
             },
             incomingValue => {
@@ -1223,7 +1232,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
         var quickScroll: string | undefined = '';
         this._disposers.scroll = reaction(
-            () => NumCast(this.layoutDoc._scrollTop),
+            () => NumCast(this.layoutDoc._layout_scrollTop),
             pos => {
                 if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) {
                     const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
@@ -1300,7 +1309,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 }
             }, 0);
             dataDoc.title = exportState.title;
-            this.dataDoc['title-custom'] = true;
+            this.dataDoc.title_custom = true;
             dataDoc.googleDocUnchanged = true;
         } else {
             delete dataDoc[GoogleRef];
@@ -1357,7 +1366,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             view.state.schema.marks.linkAnchor.create({
                                 allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }],
                                 location: 'add:right',
-                                title: `from: ${DocCast(pdfAnchor.context).title}`,
+                                title: `from: ${DocCast(pdfAnchor.embedContainer).title}`,
                                 noPreview: true,
                                 docref: false,
                             }),
@@ -1366,7 +1375,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         ]),
                     ]);
 
-                    const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { linkRelationship: 'PDF pasted' });
+                    const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { link_relationship: 'PDF pasted' });
                     if (link) {
                         view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
                     }
@@ -1454,7 +1463,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (startupText) {
                     dispatch(state.tr.insertText(startupText));
                 }
-                const textAlign = StrCast(this.dataDoc['text-align'], StrCast(Doc.UserDoc().textAlign, 'left'));
+                const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign, 'left'));
                 if (textAlign !== 'left') {
                     selectAll(this._editorView.state, tr => {
                         this._editorView!.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
@@ -1527,7 +1536,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         } else (e.nativeEvent as any).handledByInnerReactInstance = true;
 
         if (this.Document.forceActive) e.stopPropagation();
-        this.tryUpdateScrollHeight(); // if a doc a fitWidth doc is being viewed in different context (eg freeform & lightbox), then it will have conflicting heights.  so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view.
+        this.tryUpdateScrollHeight(); // if a doc a fitWidth doc is being viewed in different embedContainer (eg freeform & lightbox), then it will have conflicting heights.  so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view.
         if ((e.target as any).tagName === 'AUDIOTAG') {
             e.preventDefault();
             e.stopPropagation();
@@ -1730,7 +1739,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     setTimeout(
                         () =>
                             translateGoogleApi(result1[0], { from: 'es', to: 'en' }).then((result: any) => {
-                                this.dataDoc[this.fieldKey + '-translation'] = result1 + '\r\n\r\n' + result[0];
+                                this.dataDoc[this.fieldKey + '_translation'] = result1 + '\r\n\r\n' + result[0];
                             }),
                         1000
                     );
@@ -1740,10 +1749,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             }
             this._lastText = curText;
         }
-        if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc['title-custom']) {
+        if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc.title_custom) {
             UndoManager.RunInBatch(() => {
-                this.dataDoc['title-custom'] = true;
-                this.dataDoc.showTitle = 'title';
+                this.dataDoc.title_custom = true;
+                this.dataDoc.layout_showTitle = 'title';
                 const tr = this._editorView!.state.tr;
                 this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.rootDoc.title).length + 2))).deleteSelection());
             }, 'titler');
@@ -1803,7 +1812,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (!LinkDocPreview.LinkInfo && this._scrollRef.current) {
             if (!this.props.dontSelectOnLoad) {
                 this._ignoreScroll = true;
-                this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop;
+                this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop;
                 this._ignoreScroll = false;
                 e.stopPropagation();
                 e.preventDefault();
@@ -1823,7 +1832,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
             if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) {
                 // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
-                const setScrollHeight = () => (this.rootDoc[this.fieldKey + '-scrollHeight'] = scrollHeight);
+                const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
                 if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
                     setScrollHeight();
                 } else {
@@ -1832,21 +1841,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             }
         }
     };
-    fitContentsToBox = () => BoolCast(this.props.Document._fitContentsToBox);
-    sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+    fitContentsToBox = () => BoolCast(this.props.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._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
     };
     sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey);
     sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey);
-    setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '-height'] = height);
-    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '_height'] = height);
+    sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
     sidebarScreenToLocal = () =>
         this.props
             .ScreenToLocalTransform()
             .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0)
-            .scale(1 / NumCast(this.layoutDoc._viewScale, 1) / (this.props.NativeDimScaling?.() || 1));
+            .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this.props.NativeDimScaling?.() || 1));
 
     @computed get audioHandle() {
         return !this._recording ? null : (
@@ -1880,7 +1889,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     color,
                     opacity: annotated ? 1 : undefined,
                 }}>
-                <FontAwesomeIcon icon={'comment-alt'} />
+                <FontAwesomeIcon icon="comment-alt" />
             </div>
         );
     }
@@ -1932,25 +1941,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         fitContentsToBox={this.fitContentsToBox}
                         noSidebar={true}
                         treeViewHideTitle={true}
-                        fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}-translation` : `${this.fieldKey}-sidebar`}
+                        fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
                     />
                 </div>
             );
         };
         return (
-            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                 {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
             </div>
         );
     }
     cycleAlternateText = () => {
-        if (this.layoutDoc._showAltContentUI) {
-            const usePath = this.rootDoc[`${this.props.fieldKey}-usePath`];
-            this.rootDoc[`_${this.props.fieldKey}-usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
+        if (this.layoutDoc._layout_altContentUI) {
+            const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+            this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
         }
     };
     @computed get overlayAlternateIcon() {
-        const usePath = this.rootDoc[`${this.props.fieldKey}-usePath`];
+        const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
         return (
             <Tooltip
                 title={
@@ -1982,23 +1991,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         );
     }
     @computed get fieldKey() {
-        const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}-usePath`]);
-        return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `-${usePath.replace(':hover', '')}` : '');
+        const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}_usePath`]);
+        return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `_${usePath.replace(':hover', '')}` : '');
     }
     @observable _isHovering = false;
     render() {
         TraceMobx();
         const active = this.props.isContentActive() || this.props.isSelected();
         const selected = active;
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
         const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
         const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-        const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
-        const selPaddingClass = selected && !this.layoutDoc._singleLine && paddingY >= 10 ? '-selected' : '';
+        const selPad = (selected && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
+        const selPaddingClass = selected && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
         const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
         return styleFromLayoutString?.height === '0px' ? null : (
             <div
@@ -2010,7 +2019,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
                         (e: WheelEvent) => {
                             if (this.props.isContentActive() && !this.props.allowScroll) {
-                                if (!NumCast(this.layoutDoc._scrollTop) && e.deltaY <= 0) e.preventDefault();
+                                if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault();
                                 e.stopPropagation();
                             }
                         },
@@ -2028,7 +2037,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                           }),
                     display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
                     transition: 'inherit',
-                    // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
+                    // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
                     color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
                     fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
                     fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily),
@@ -2039,8 +2048,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     className="formattedTextBox-cont"
                     ref={this._ref}
                     style={{
-                        overflow: this.autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an autoHeight doc in its own tab, or in the lightbox
-                        height: this.props.height || (this.autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
+                        overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
+                        height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
                         pointerEvents: interactive ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
@@ -2056,9 +2065,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
                         ref={this._scrollRef}
                         style={{
-                            width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
+                            width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
                             pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
-                            overflow: this.layoutDoc._singleLine ? 'hidden' : this.layoutDoc._autoHeight ? 'visible' : undefined,
+                            overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
                         }}
                         onScroll={this.onScroll}
                         onDrop={this.ondrop}>
@@ -2075,10 +2084,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             }}
                         />
                     </div>
-                    {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
-                    {this.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
+                    {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+                    {this.noSidebar || this.Document._layout_noSidebar || this.props.dontSelectOnLoad || this.Document._createDocOnCR ? null : this.sidebarHandle}
                     {this.audioHandle}
-                    {this.layoutDoc._showAltContentUI ? this.overlayAlternateIcon : null}
+                    {this.layoutDoc._layout_altContentUI ? this.overlayAlternateIcon : null}
                 </div>
             </div>
         );
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index f0caa1f4f..5e0041b84 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -623,15 +623,15 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                     if (linkclicked) {
                         const linkDoc = await DocServer.GetRefField(linkclicked);
                         if (linkDoc instanceof Doc) {
-                            const anchor1 = await Cast(linkDoc.anchor1, Doc);
-                            const anchor2 = await Cast(linkDoc.anchor2, Doc);
+                            const link_anchor_1 = await Cast(linkDoc.link_anchor_1, Doc);
+                            const link_anchor_2 = await Cast(linkDoc.link_anchor_2, Doc);
                             const currentDoc = SelectionManager.Docs().lastElement();
-                            if (currentDoc && anchor1 && anchor2) {
-                                if (Doc.AreProtosEqual(currentDoc, anchor1)) {
-                                    return StrCast(anchor2.title);
+                            if (currentDoc && link_anchor_1 && link_anchor_2) {
+                                if (Doc.AreProtosEqual(currentDoc, link_anchor_1)) {
+                                    return StrCast(link_anchor_2.title);
                                 }
-                                if (Doc.AreProtosEqual(currentDoc, anchor2)) {
-                                    return StrCast(anchor1.title);
+                                if (Doc.AreProtosEqual(currentDoc, link_anchor_2)) {
+                                    return StrCast(link_anchor_1.title);
                                 }
                             }
                         }
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index cad56b14b..104aed058 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -81,9 +81,9 @@ export class RichTextRules {
                 textDoc.inlineTextCount = numInlines + 1;
                 const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to
                 const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
-                const textDocInline = Docs.Create.TextDocument('', { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _fitWidth: true, _autoHeight: true, _fontSize: '9px', title: 'inline comment' });
+                const textDocInline = Docs.Create.TextDocument('', { _layout_fieldKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _layout_fitWidth: true, _layout_autoHeight: true, _fontSize: '9px', title: 'inline comment' });
                 textDocInline.title = inlineFieldKey; // give the annotation its own title
-                textDocInline['title-custom'] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
+                textDocInline.title_custom = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
                 textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
                 textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
                 textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
@@ -249,7 +249,7 @@ export class RichTextRules {
                         this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
                     }
 
-                    DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { linkRelationship: 'portal to:portal from' });
+                    DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
 
                     const fstate = this.TextBox.EditorView?.state;
                     if (fstate && selection) {
@@ -299,28 +299,6 @@ export class RichTextRules {
                 return tr.setSelection(new NodeSelection(tr.doc.resolve(tr.selection.$from.pos - 1)));
             }),
 
-            // create an inline view of a document {{ <layoutKey> : <Doc> }}
-            // {{:Doc}} => show default view of document
-            // {{<layout>}} => show layout for this doc
-            // {{<layout> : Doc}} => show layout for another doc
-            new InputRule(new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_@\.\? \-0-9]+)?\}\}$/), (state, match, start, end) => {
-                const fieldKey = match[1] || '';
-                const fieldParam = match[2]?.replace('…', '...') || '';
-                const rawdocid = match[3]?.substring(1);
-                const docId = rawdocid ? (!rawdocid.includes('@') ? normalizeEmail(Doc.CurrentUserEmail) + '@' + rawdocid : rawdocid) : undefined;
-                if (!fieldKey && !docId) return state.tr;
-                docId &&
-                    DocServer.GetRefField(docId).then(docx => {
-                        if (!(docx instanceof Doc && docx)) {
-                            Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docId);
-                        }
-                    });
-                const node = (state.doc.resolve(start) as any).nodeAfter;
-                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: 'dashDoc', docId, fieldKey: fieldKey + fieldParam, float: 'unset', alias: Utils.GenerateGuid() });
-                const sm = state.storedMarks || undefined;
-                return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
-            }),
-
             // create an inline view of a tag stored under the '#' field
             new InputRule(new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/), (state, match, start, end) => {
                 const tag = match[1];
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 6c9d5d31a..f27fb18e2 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -247,7 +247,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             hidden: { default: false }, // whether dashComment node has toggle the dashDoc's display off
             fieldKey: { default: '' },
             docId: { default: '' },
-            alias: { default: '' },
+            embedding: { default: '' },
         },
         group: 'inline',
         draggable: false,
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index c22107c03..858d83b7a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -273,7 +273,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     progressivizedItems = (doc: Doc) => {
         const targetList = PresBox.targetRenderedDoc(doc);
         if (doc.presIndexed !== undefined && targetList) {
-            const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '-annotations']);
+            const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '_annotations']);
             return listItems.filter(doc => !doc.unrendered);
         }
     };
@@ -419,7 +419,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
         if (activeFrame !== undefined) {
             const transTime = NumCast(activeItem.presTransition, 500);
-            const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc);
+            const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).embedContainer) : DocCast(activeItem.presentationTargetDoc);
             const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
             if (context) {
                 const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
@@ -435,7 +435,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const setData = bestTargetView?.ComponentView?.setData;
             if (setData) setData(activeItem.presData);
             else Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
-            bestTarget[fkey + '-usePath'] = activeItem.presUsePath;
+            bestTarget[fkey + '_usePath'] = activeItem.presUsePath;
             setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
         }
         if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) {
@@ -449,14 +449,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presClipWidth !== undefined)) {
-            if (bestTarget._clipWidth !== activeItem.presClipWidth) {
-                bestTarget._clipWidth = activeItem.presClipWidth;
+            const fkey = '_' + Doc.LayoutFieldKey(bestTarget);
+            if (bestTarget[fkey + '_clipWidth'] !== activeItem.presClipWidth) {
+                bestTarget[fkey + '_clipWidth'] = activeItem.presClipWidth;
                 changed = true;
             }
         }
         if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
-            if (bestTarget._currentTimecode !== activeItem.presStartTime) {
-                bestTarget._currentTimecode = activeItem.presStartTime;
+            if (bestTarget._layout_currentTimecode !== activeItem.presStartTime) {
+                bestTarget._layout_currentTimecode = activeItem.presStartTime;
                 changed = true;
             }
         }
@@ -504,8 +505,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
 
         if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presViewScroll !== undefined)) {
-            if (bestTarget._scrollTop !== activeItem.presViewScroll) {
-                bestTarget._scrollTop = activeItem.presViewScroll;
+            if (bestTarget._layout_scrollTop !== activeItem.presViewScroll) {
+                bestTarget._layout_scrollTop = activeItem.presViewScroll;
                 changed = true;
                 const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
                 if (contentBounds) {
@@ -516,19 +517,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.presAnnotations !== undefined)) {
             const fkey = Doc.LayoutFieldKey(bestTarget);
-            const oldItems = DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered);
+            const oldItems = DocListCast(bestTarget[fkey + '_annotations']).filter(doc => doc.unrendered);
             const newItems = DocListCast(activeItem.presAnnotations).map(doc => {
                 doc.hidden = false;
                 return doc;
             });
-            const hiddenItems = DocListCast(bestTarget[fkey + '-annotations'])
+            const hiddenItems = DocListCast(bestTarget[fkey + '_annotations'])
                 .filter(doc => !doc.unrendered && !newItems.includes(doc))
                 .map(doc => {
                     doc.hidden = true;
                     return doc;
                 });
             const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
-            Doc.GetProto(bestTarget)[fkey + '-annotations'] = newList;
+            Doc.GetProto(bestTarget)[fkey + '_annotations'] = newList;
         }
         if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) {
             changed = true;
@@ -560,20 +561,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
             if (contentBounds) {
                 const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
-                bestTarget._panX = viewport.panX;
-                bestTarget._panY = viewport.panY;
+                bestTarget._freeform_panX = viewport.panX;
+                bestTarget._freeform_panY = viewport.panY;
                 const dv = DocumentManager.Instance.getDocumentView(bestTarget);
                 if (dv) {
                     changed = true;
                     const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
-                    activeItem.presMovement === PresMovement.Zoom && (bestTarget._viewScale = computedScale);
+                    activeItem.presMovement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale);
                     dv.ComponentView?.brushView?.(viewport);
                 }
             } else {
-                if (bestTarget._panX !== activeItem.presPanX || bestTarget._panY !== activeItem.presPanY || bestTarget._viewScale !== activeItem.presViewScale) {
-                    bestTarget._panX = activeItem.presPanX ?? bestTarget._panX;
-                    bestTarget._panY = activeItem.presPanY ?? bestTarget._panY;
-                    bestTarget._viewScale = activeItem.presViewScale ?? bestTarget._viewScale;
+                if (bestTarget._freeform_panX !== activeItem.presPanX || bestTarget._freeform_panY !== activeItem.presPanY || bestTarget._freeform_scale !== activeItem.presViewScale) {
+                    bestTarget._freeform_panX = activeItem.presPanX ?? bestTarget._freeform_panX;
+                    bestTarget._freeform_panY = activeItem.presPanY ?? bestTarget._freeform_panY;
+                    bestTarget._freeform_scale = activeItem.presViewScale ?? bestTarget._freeform_scale;
                     changed = true;
                 }
             }
@@ -610,12 +611,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinProps?.activeFrame !== undefined;
             const fkey = Doc.LayoutFieldKey(targetDoc);
             if (pinProps.pinData.dataview) {
-                pinDoc.presUsePath = targetDoc[fkey + '-usePath'];
+                pinDoc.presUsePath = targetDoc[fkey + '_usePath'];
                 pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
             }
             if (pinProps.pinData.dataannos) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
-                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered));
+                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '_annotations']).filter(doc => !doc.unrendered));
             }
             if (pinProps.pinData.inkable) {
                 pinDoc.presFillColor = targetDoc.fillColor;
@@ -623,8 +624,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinDoc.presWidth = targetDoc._width;
                 pinDoc.presHeight = targetDoc._height;
             }
-            if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._scrollTop;
-            if (pinProps.pinData.clippable) pinDoc.presClipWidth = targetDoc._clipWidth;
+            if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._layout_scrollTop;
+            if (pinProps.pinData.clippable) {
+                const fkey = Doc.LayoutFieldKey(targetDoc);
+                pinDoc.presClipWidth = targetDoc[fkey + '_clipWidth'];
+            }
             if (pinProps.pinData.datarange) {
                 pinDoc.presXRange = undefined; //targetDoc?.xrange;
                 pinDoc.presYRange = undefined; //targetDoc?.yrange;
@@ -649,13 +653,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField);
             if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField;
             if (pinProps.pinData.pannable) {
-                pinDoc.presPanX = NumCast(targetDoc._panX);
-                pinDoc.presPanY = NumCast(targetDoc._panY);
-                pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1);
+                pinDoc.presPanX = NumCast(targetDoc._freeform_panX);
+                pinDoc.presPanY = NumCast(targetDoc._freeform_panY);
+                pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
             }
             if (pinProps.pinData.temporal) {
-                pinDoc.presStartTime = targetDoc._currentTimecode;
-                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(targetDoc.presStartTime) + 0.1);
+                pinDoc.presStartTime = targetDoc._layout_currentTimecode;
+                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.presStartTime) + 0.1);
                 pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration);
             }
         }
@@ -663,7 +667,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // If pinWithView option set then update scale and x / y props of slide
             const bounds = pinProps.pinViewport;
             pinDoc.presPinView = true;
-            pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1);
+            pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
             pinDoc.presPanX = bounds.left + bounds.width / 2;
             pinDoc.presPanY = bounds.top + bounds.height / 2;
             pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
@@ -793,7 +797,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const savedStates = docs.map(doc => {
             switch (doc.type) {
                 case DocumentType.COL:
-                    if (doc._viewType === CollectionViewType.Freeform) return { type: CollectionViewType.Freeform, doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) };
+                    if (doc._viewType === CollectionViewType.Freeform) return { type: CollectionViewType.Freeform, doc, x: NumCast(doc.freeform_panX), y: NumCast(doc.freeform_panY), s: NumCast(doc.freeform_scale) };
                     break;
                 case DocumentType.INK:
                     if (doc.data instanceof InkField) {
@@ -813,7 +817,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 const { x, y, s, doc } = savedState!;
                                 doc._panX = x;
                                 doc._panY = y;
-                                doc._viewScale = s;
+                                doc._freeform_scale = s;
                             }
                             break;
                         case DocumentType.INK:
@@ -1008,17 +1012,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     return false;
                 }
             } else {
-                if (!doc.aliasOf) {
-                    const original = Doc.MakeAlias(doc);
-                    TabDocView.PinDoc(original, {});
-                    setTimeout(() => this.removeDocument(doc), 0);
-                    return false;
-                } else {
-                    if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
-                    doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
-                    doc.presMovement = PresMovement.Zoom;
-                    if (this._expandBoolean) doc.presExpandInlineButton = true;
-                }
+                if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
+                doc.presentationTargetDoc = doc.createdFrom; // dropped document will be a new embedding of an embedded document somewhere else.
+                doc.presMovement = PresMovement.Zoom;
+                if (this._expandBoolean) doc.presExpandInlineButton = true;
             }
         });
         return true;
@@ -1086,7 +1083,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 this.rootDoc._itemIndex = index;
             }
         } else this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
-        this.updateCurrentPresentation(DocCast(doc.context));
+        this.updateCurrentPresentation(DocCast(doc.embedContainer));
     };
 
     //Command click
@@ -1240,7 +1237,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
             .forEach((doc, index) => {
                 const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
-                const srcContext = Cast(tagDoc.context, Doc, null);
+                const srcContext = Cast(tagDoc.embedContainer, Doc, null);
                 const width = NumCast(tagDoc._width) / 10;
                 const height = Math.max(NumCast(tagDoc._height) / 10, 15);
                 const edge = Math.max(width, height);
@@ -1300,7 +1297,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Method called for viewing paths which adds a single line with
      * points at the center of each document added.
-     * Design choice: When this is called it sets _fitContentsToBox as true so the
+     * Design choice: When this is called it sets _freeform_fitContentsToBox as true so the
      * user can have an overview of all of the documents in the collection.
      * (Design needed for when documents in presentation trail are in another
      * collection)
@@ -1553,7 +1550,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
                                 // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
                                 let dataField = Doc.LayoutFieldKey(tagDoc);
-                                if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '-annotations';
+                                if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
 
                                 if (DocCast(activeItem.presentationTargetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc.annotationOn["${dataField}"]`);
                                 else activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc["${dataField}"]`);
@@ -1743,7 +1740,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem = this.activeItem;
         if (activeItem && this.targetDoc) {
             const clipStart = NumCast(activeItem.clipStart);
-            const clipEnd = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '-duration']));
+            const clipEnd = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '_duration']));
             return (
                 <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                     <div>
@@ -2077,10 +2074,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // prettier-ignore
         switch (layout) {
             case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
-            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
-            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
-            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
-            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x, y })
+            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
+            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
+            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
+            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y })
         }
     };
 
@@ -2458,7 +2455,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 childIgnoreNativeSize={true}
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
-                                //childFitWidth={returnTrue}
+                                //childLayoutFitWidth={returnTrue}
                                 childOpacity={returnOne}
                                 //childLayoutString={PresElementBox.LayoutString('data')}
                                 childClickScript={PresBox.navigateToDocScript}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 62e5314c3..e48d2d674 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -61,7 +61,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     componentDidMount() {
-        this.layoutDoc.hideLinkButton = true;
+        this.layoutDoc.layout_hideLinkButton = true;
         this._heightDisposer = reaction(
             () => ({ expand: this.rootDoc.presExpandInlineButton, height: this.collapsedHeight }),
             ({ expand, height }) => (this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0)),
@@ -319,7 +319,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // a previously recorded video will have timecode defined
     static videoIsRecorded = (activeItem: Opt<Doc>) => {
         const casted = Cast(activeItem?.recording, Doc, null);
-        return casted && 'currentTimecode' in casted;
+        return casted && 'layout_currentTimecode' in casted;
     };
 
     removeAllRecordingInOverlay = () => {
@@ -380,10 +380,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 _width: 384,
                 _height: 216,
                 hideDocumentButtonBar: true,
-                hideDecorationTitle: true,
+                layout_hideDecorationTitle: true,
                 hideOpenButton: true,
                 // hideDeleteButton: true,
-                cloneFieldFilter: new List<string>(['system']),
+                cloneFieldFilter: new List<string>(['isSystem']),
             });
 
             // attach the recording to the slide, and attach the slide to the recording
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index cc0072a94..8bd060d4f 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -51,8 +51,8 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         const newDoc = Docs.Create.TextDocument(this.props.text.trim(), {
             _width: 200,
             _height: 50,
-            _fitWidth: true,
-            _autoHeight: true,
+            _layout_fitWidth: true,
+            _layout_autoHeight: true,
         });
         this.addDoc(newDoc, this.sidebarId);
     };
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index ea709a859..40b745cc4 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -90,7 +90,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @observable isAnnotating = false;
     // key where data is stored
     @computed get allAnnotations() {
-        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']), this.props.docFilters(), this.props.docRangeFilters());
+        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.props.docFilters(), this.props.docRangeFilters());
     }
     @computed get inlineTextAnnotations() {
         return this.allAnnotations.filter(a => a.textInlineAnnotations);
@@ -101,12 +101,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.setupPdfJsViewer();
         this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0));
 
-        this._disposers.autoHeight = reaction(
-            () => this.props.layoutDoc._autoHeight,
-            autoHeight => {
-                if (autoHeight) {
-                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
+        this._disposers.layout_autoHeight = reaction(
+            () => this.props.layoutDoc._layout_autoHeight,
+            layout_autoHeight => {
+                if (layout_autoHeight) {
+                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -123,7 +123,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             { fireImmediately: true }
         );
         this._disposers.curPage = reaction(
-            () => Cast(this.props.Document._curPage, 'number', null),
+            () => Cast(this.props.Document._layout_curPage, 'number', null),
             page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
             { fireImmediately: true }
         );
@@ -181,14 +181,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
-            if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
+            const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
+            if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._layout_scrollTop) {
                 if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc };
                 else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper);
                 else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) });
             }
         } else {
-            this._initialScroll = { loc: NumCast(this.props.layoutDoc._scrollTop), easeFunc: options.easeFunc };
+            this._initialScroll = { loc: NumCast(this.props.layoutDoc._layout_scrollTop), easeFunc: options.easeFunc };
         }
         return focusSpeed;
     };
@@ -208,18 +208,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     pagesinit = () => {
         if (this._pdfViewer._setDocumentViewerElement?.offsetParent) {
-            runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1));
-            this.gotoPage(NumCast(this.props.Document._curPage, 1));
+            runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._freeform_scale = 1));
+            this.gotoPage(NumCast(this.props.Document._layout_curPage, 1));
         }
         document.removeEventListener('pagesinit', this.pagesinit);
         var quickScroll: { loc?: string; easeFunc?: 'ease' | 'linear' } | undefined = { loc: this._initialScroll ? this._initialScroll.loc?.toString() : '', easeFunc: this._initialScroll ? this._initialScroll.easeFunc : undefined };
         this._disposers.scale = reaction(
-            () => NumCast(this.props.layoutDoc._viewScale, 1),
+            () => NumCast(this.props.layoutDoc._freeform_scale, 1),
             scale => (this._pdfViewer.currentScaleValue = scale),
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(
-            () => Math.abs(NumCast(this.props.Document._scrollTop)),
+            () => Math.abs(NumCast(this.props.Document._layout_scrollTop)),
             pos => {
                 if (!this._ignoreScroll) {
                     this._showWaiting && this.setupPdfJsViewer();
@@ -316,7 +316,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if (this._mainCont.current && !this._forcedScroll) {
             this._ignoreScroll = true; // the pdf scrolled, so we need to tell the Doc to scroll but we don't want the doc to then try to set the PDF scroll pos (which would interfere with the smooth scroll animation)
             if (!LinkDocPreview.LinkInfo) {
-                this.props.layoutDoc._scrollTop = this._mainCont.current.scrollTop;
+                this.props.layoutDoc._layout_scrollTop = this._mainCont.current.scrollTop;
             }
             this._ignoreScroll = false;
             if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio
@@ -371,7 +371,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         // if alt+left click, drag and annotate
         this._downX = e.clientX;
         this._downY = e.clientY;
-        if ((this.props.Document._viewScale || 1) !== 1) return;
+        if ((this.props.Document._freeform_scale || 1) !== 1) return;
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
@@ -421,7 +421,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
 
         // Changing which document to add the annotation to (the currently selected PDF)
-        GPTPopup.Instance.setSidebarId('data-sidebar');
+        GPTPopup.Instance.setSidebarId('data_sidebar');
         GPTPopup.Instance.addDoc = this.props.sidebarAddDoc;
     };
 
@@ -434,7 +434,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 const rect = clientRects.item(i);
                 if (rect?.width && rect.width < this._mainCont.current.clientWidth / this.props.ScreenToLocalTransform().Scale) {
                     const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
-                    const pdfScale = NumCast(this.props.layoutDoc._viewScale, 1);
+                    const pdfScale = NumCast(this.props.layoutDoc._freeform_scale, 1);
                     const annoBox = document.createElement('div');
                     annoBox.className = 'marqueeAnnotator-annotationBox';
                     // transforms the positions from screen onto the pdf div
@@ -477,7 +477,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             if (e.ctrlKey) {
                 const curScale = Number(this._pdfViewer.currentScaleValue);
                 this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - (curScale * e.deltaY) / 1000));
-                this.props.layoutDoc._viewScale = Number(this._pdfViewer.currentScaleValue);
+                this.props.layoutDoc._freeform_scale = Number(this._pdfViewer.currentScaleValue);
             }
         }
     };
@@ -485,12 +485,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
     pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
     @computed get annotationLayer() {
         return (
-            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}>
+            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
                 {this.inlineTextAnnotations
                     .sort((a, b) => NumCast(a.y) - NumCast(b.y))
                     .filter(anno => !anno.hidden)
                     .map(anno => (
-                        <Annotation {...this.props} fieldKey={this.props.fieldKey + '-annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                        <Annotation {...this.props} fieldKey={this.props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
                     ))}
             </div>
         );
@@ -508,8 +508,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     getScrollHeight = () => this._scrollHeight;
     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
-    scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform());
-    overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
+    scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._layout_scrollTop)) : this.props.ScreenToLocalTransform());
+    overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._freeform_scale, 1));
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
@@ -539,7 +539,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 childPointerEvents="all" // but freeform children need to get events to allow text editing, etc
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
-                fieldKey={this.props.fieldKey + '-annotations'}
+                fieldKey={this.props.fieldKey + '_annotations'}
                 getScrollHeight={this.getScrollHeight}
                 setPreviewCursor={this.setPreviewCursor}
                 setBrushViewer={this.setBrushViewer}
@@ -548,7 +548,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 ScreenToLocalTransform={this.overlayTransform}
                 isAnyChildContentActive={returnFalse}
                 isAnnotationOverlayScrollable={true}
-                dropAction={'alias'}
+                dropAction="embed"
                 docFilters={docFilters}
                 select={emptyFunction}
                 bringToFront={emptyFunction}
@@ -586,8 +586,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     onPointerDown={this.onPointerDown}
                     onClick={this.onClick}
                     style={{
-                        overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? 'scroll' : undefined,
-                        height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `100%`,
+                        overflowX: NumCast(this.props.layoutDoc._freeform_scale, 1) !== 1 ? 'scroll' : undefined,
+                        height: !this.props.Document._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `100%`,
                     }}>
                     {this.pdfViewerDiv}
                     {this.annotationLayer}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 32b661bb6..536ec7c75 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -47,7 +47,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     @observable _linkedDocsIn: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
     @observable _selectedResult: Doc | undefined = undefined;
     @observable _deletedDocsStatus: boolean = false;
-    @observable _onlyAliases: boolean = true;
+    @observable _onlyEmbeddings: boolean = true;
 
     /**
      * This is the constructor for the SearchBox class.
@@ -139,7 +139,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 visited.push(d);
                 const fieldKey = Doc.LayoutFieldKey(d);
                 const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
-                const data = d[annos ? fieldKey + '-annotations' : fieldKey];
+                const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                 data && newarray.push(...DocListCast(data));
                 func(depth, d);
             });
@@ -166,7 +166,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                     .map(async d => {
                         const fieldKey = Doc.LayoutFieldKey(d);
                         const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
-                        const data = d[annos ? fieldKey + '-annotations' : fieldKey];
+                        const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                         const docs = await DocListCastAsync(data);
                         docs && newarray.push(...docs);
                         func(depth, d);
@@ -217,34 +217,34 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             'y',
             'proto',
             'width',
-            'autoHeight',
+            'layout_autoHeight',
             'acl-Override',
             'acl-Public',
-            'context',
+            'embedContainer',
             'zIndex',
             'height',
-            'text-scrollHeight',
-            'text-height',
+            'text_scrollHeight',
+            'text_height',
             'cloneFieldFilter',
-            'isPrototype',
-            'text-annotations',
-            'dragFactory-count',
-            'text-noTemplate',
-            'aliases',
-            'system',
-            'layoutKey',
-            'baseProto',
+            'isDataDoc',
+            'text_annotations',
+            'dragFactory_count',
+            'text_noTemplate',
+            'proto_embeddings',
+            'isSystem',
+            'layout_fieldKey',
+            'isBaseProto',
             'xMargin',
             'yMargin',
             'links',
             'layout',
             'layout_keyValue',
-            'fitWidth',
+            'layout_fitWidth',
             'viewType',
-            'title-custom',
-            'panX',
-            'panY',
-            'viewScale',
+            'title_custom',
+            'freeform_panX',
+            'freeform_panY',
+            'freeform_scale',
         ];
         query = query.toLowerCase();
 
@@ -300,8 +300,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 const linkedDocSet = new Set<Doc>();
 
                 Doc.GetProto(doc)[DirectLinksSym].forEach(link => {
-                    const d1 = link?.anchor1 as Doc;
-                    const d2 = link?.anchor2 as Doc;
+                    const d1 = link?.link_anchor_1 as Doc;
+                    const d2 = link?.link_anchor_2 as Doc;
                     if (doc === d1 && this._results.has(d2)) {
                         linkedDocSet.add(d2);
                         this._linkedDocsIn.get(d2)?.add(doc);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5e3e27a7c..deda4c876 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -236,7 +236,7 @@ export class Doc extends RefField {
         if (
             doc &&
             Doc.MyFileOrphans instanceof Doc &&
-            Doc.IsPrototype(doc) &&
+            Doc.IsDocDataProto(doc) &&
             !Doc.IsSystem(doc) &&
             ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
             !doc.isFolder &&
@@ -381,7 +381,7 @@ export class Doc extends RefField {
         const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
         if (templateLayoutDoc) {
             let renderFieldKey: any;
-            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, 'layout')];
+            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
             if (typeof layoutField === 'string') {
                 renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0]; //layoutField.split("'")[1];
             } else {
@@ -510,14 +510,14 @@ export namespace Doc {
     export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
         return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
     }
-    export function IsPrototype(doc: Doc) {
-        return GetT(doc, 'isPrototype', 'boolean', true);
+    export function IsDocDataProto(doc: Doc) {
+        return GetT(doc, 'isDataDoc', 'boolean', true);
     }
     export function IsBaseProto(doc: Doc) {
-        return GetT(doc, 'baseProto', 'boolean', true);
+        return GetT(doc, 'isBaseProto', 'boolean', true);
     }
     export function IsSystem(doc: Doc) {
-        return GetT(doc, 'system', 'boolean', true);
+        return GetT(doc, 'isSystem', 'boolean', true);
     }
     export function IsDelegateField(doc: Doc, fieldKey: string) {
         return doc && Get(doc, fieldKey, true) !== undefined;
@@ -574,9 +574,9 @@ export namespace Doc {
 
     // Gets the data document for the document.  Note: this is mis-named -- it does not specifically
     // return the doc's proto, but rather recursively searches through the proto inheritance chain
-    // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype').
+    // and returns the document who's proto is undefined or whose proto is marked as a data doc ('isDataDoc').
     export function GetProto(doc: Doc): Doc {
-        const proto = doc && (Doc.GetT(doc, 'isPrototype', 'boolean', true) ? doc : doc.proto || doc);
+        const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : doc.proto || doc);
         return proto === doc ? proto : Doc.GetProto(proto);
     }
     export function GetDataDoc(doc: Doc): Doc {
@@ -680,25 +680,25 @@ export namespace Doc {
         return bounds;
     }
 
-    export function MakeAlias(doc: Doc, id?: string) {
-        const alias = !GetT(doc, 'isPrototype', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
-        const layout = Doc.LayoutField(alias);
-        if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) {
-            Doc.SetLayout(alias, Doc.MakeAlias(layout));
+    export function MakeEmbedding(doc: Doc, id?: string) {
+        const embedding = !GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
+        const layout = Doc.LayoutField(embedding);
+        if (layout instanceof Doc && layout !== embedding && layout === Doc.Layout(embedding)) {
+            Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
         }
-        alias.aliasOf = doc;
-        alias.aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
-        alias.title = ComputedField.MakeFunction(`renameAlias(this)`);
-        alias.author = Doc.CurrentUserEmail;
+        embedding.createdFrom = doc;
+        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = NumCast(Doc.GetProto(doc).proto_embeddingId) + 1;
+        embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
+        embedding.author = Doc.CurrentUserEmail;
 
-        Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'aliases', alias);
+        Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'proto_embeddings', embedding);
 
-        return alias;
+        return embedding;
     }
 
-    export function BestAlias(doc: Doc) {
-        const bestAlias = Doc.GetProto(doc) ? DocListCast(doc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : doc;
-        return bestAlias ?? Doc.MakeAlias(doc);
+    export function BestEmbedding(doc: Doc) {
+        const bestEmbedding = Doc.GetProto(doc) ? DocListCast(doc.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+        return bestEmbedding ?? Doc.MakeEmbedding(doc);
     }
 
     // this lists out all the tag ids that can be  in a RichTextField that might contain document ids.
@@ -745,7 +745,7 @@ export namespace Doc {
                 if (docAtKey instanceof Doc) {
                     if (pruneDocs.includes(docAtKey)) {
                         // prune doc and do nothing
-                    } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['context', 'annotationOn', 'proto'].includes(key) || ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) {
+                    } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['embedContainer', 'annotationOn', 'proto'].includes(key) || ((key === 'link_anchor_1' || key === 'link_anchor_2') && doc.author === Doc.CurrentUserEmail))) {
                         assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
                     } else {
                         assignKey(docAtKey);
@@ -766,7 +766,8 @@ export namespace Doc {
         Array.from(doc[DirectLinksSym]).forEach(async link => {
             if (
                 cloneLinks ||
-                ((cloneMap.has(DocCast(link.anchor1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.anchor1)?.annotationOn)?.[Id])) && (cloneMap.has(DocCast(link.anchor2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.anchor2)?.annotationOn)?.[Id])))
+                ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) &&
+                    (cloneMap.has(DocCast(link.link_anchor_2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id])))
             ) {
                 linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
             }
@@ -804,7 +805,7 @@ export namespace Doc {
     export async function MakeClone(doc: Doc, cloneLinks = true, cloneMap: Map<string, Doc> = new Map()) {
         const linkMap = new Map<string, Doc>();
         const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
-        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.context ? [DocCast(doc.context)] : [], cloneLinks);
+        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks);
         const repaired = new Set<Doc>();
         const linkedDocs = Array.from(linkMap.values());
         linkedDocs.map((link: Doc) => LinkManager.Instance.addLink(link, true));
@@ -980,7 +981,7 @@ export namespace Doc {
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
         const copy = new Doc(copyProtoId, true);
         updateCachedAcls(copy);
-        const exclude = Cast(doc.cloneFieldFilter, listSpec('string'), []);
+        const exclude = [...Cast(doc.cloneFieldFilter, listSpec('string'), []), 'dragFactory_count', 'cloneFieldFilter'];
         Object.keys(doc).forEach(key => {
             if (exclude.includes(key)) return;
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
@@ -1009,12 +1010,12 @@ export namespace Doc {
             }
         });
         if (copyProto) {
-            Doc.GetProto(copy).context = undefined;
-            Doc.GetProto(copy).aliases = new List<Doc>([copy]);
+            Doc.GetProto(copy).embedContainer = undefined;
+            Doc.GetProto(copy).proto_embeddings = new List<Doc>([copy]);
         } else {
-            Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'aliases', copy);
+            Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'proto_embeddings', copy);
         }
-        copy.context = undefined;
+        copy.embedContainer = undefined;
         Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
@@ -1035,7 +1036,7 @@ export namespace Doc {
             Object.keys(doc)
                 .filter(key => key.startsWith('acl'))
                 .forEach(key => (delegate[key] = doc[key]));
-            if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'aliases', delegate);
+            if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'proto_embeddings', delegate);
             title && (delegate.title = title);
             delegate[Initializing] = false;
             Doc.AddFileOrphan(delegate);
@@ -1053,13 +1054,13 @@ export namespace Doc {
         delegateProto[Initializing] = true;
         delegateProto.proto = doc;
         delegateProto.author = Doc.CurrentUserEmail;
-        delegateProto.isPrototype = true;
+        delegateProto.isDataDoc = true;
         title && (delegateProto.title = title);
         const delegate = new Doc(id, true);
         delegate[Initializing] = true;
         delegate.proto = delegateProto;
         delegate.author = Doc.CurrentUserEmail;
-        Doc.AddDocToList(delegateProto[DataSym], 'aliases', delegate);
+        Doc.AddDocToList(delegateProto[DataSym], 'proto_embeddings', delegate);
         delegate[Initializing] = false;
         delegateProto[Initializing] = false;
         return delegate;
@@ -1071,9 +1072,9 @@ export namespace Doc {
             const proto = new Doc();
             proto.author = Doc.CurrentUserEmail;
             const target = Doc.MakeDelegate(proto);
-            const targetKey = StrCast(templateDoc.layoutKey, 'layout');
+            const targetKey = StrCast(templateDoc.layout_fieldKey, 'layout');
             const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
-            target.layoutKey = targetKey;
+            target.layout_fieldKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
             Doc.defaultAclPrivate && (applied['acl-Public'] = 'Not Shared');
             return applied;
@@ -1176,31 +1177,31 @@ export namespace Doc {
         return overrideLayout || doc[LayoutSym] || doc;
     }
     export function SetLayout(doc: Doc, layout: Doc | string) {
-        doc[StrCast(doc.layoutKey, 'layout')] = layout;
+        doc[StrCast(doc.layout_fieldKey, 'layout')] = layout;
     }
     export function LayoutField(doc: Doc) {
-        return doc[StrCast(doc.layoutKey, 'layout')];
+        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 layoutKey
+        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 NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
         return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
     }
     export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
-        return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeWidth'], useWidth ? doc[WidthSym]() : 0));
+        return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? doc[WidthSym]() : 0));
     }
     export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
         if (!doc) return 0;
         const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]()) / doc[WidthSym]();
-        const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeHeight'], useHeight ? doc[HeightSym]() : 0);
+        const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? doc[HeightSym]() : 0);
         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 DocData();
@@ -1271,8 +1272,8 @@ export namespace Doc {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
             if (lastBrushed) {
                 for (const link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
-                    const a1 = Cast(link.anchor1, Doc, null);
-                    const a2 = Cast(link.anchor2, Doc, null);
+                    const a1 = Cast(link.link_anchor_1, Doc, null);
+                    const a2 = Cast(link.link_anchor_2, Doc, null);
                     if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) || Doc.AreProtosEqual(Cast(a1.annotationOn, Doc, null), doc) || Doc.AreProtosEqual(Cast(a2.annotationOn, Doc, null), doc)) {
                         return DocBrushStatus.linkHighlighted;
                     }
@@ -1304,8 +1305,8 @@ export namespace Doc {
     }
 
     export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) {
-        if (linkDoc.anchor2 === anchorDoc || (linkDoc.anchor2 as Doc).annotationOn) return '2';
-        return Doc.AreProtosEqual(anchorDoc, (linkDoc.anchor1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.anchor1 as Doc) ? '1' : '2';
+        if (linkDoc.link_anchor_2 === anchorDoc || (linkDoc.link_anchor_2 as Doc).annotationOn) return '2';
+        return Doc.AreProtosEqual(anchorDoc, (linkDoc.link_anchor_1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.link_anchor_1 as Doc) ? '1' : '2';
     }
 
     export function linkFollowUnhighlight() {
@@ -1400,14 +1401,14 @@ export namespace Doc {
     }
 
     export function deiconifyView(doc: Doc) {
-        StrCast(doc.layoutKey).split('_')[1] === 'icon' && setNativeView(doc);
+        StrCast(doc.layout_fieldKey).split('_')[1] === 'icon' && setNativeView(doc);
     }
 
     export function setNativeView(doc: any) {
-        const prevLayout = StrCast(doc.layoutKey).split('_')[1];
+        const prevLayout = StrCast(doc.layout_fieldKey).split('_')[1];
         const deiconify = prevLayout === 'icon' && StrCast(doc.deiconifyLayout) ? 'layout_' + StrCast(doc.deiconifyLayout) : '';
         prevLayout === 'icon' && (doc.deiconifyLayout = undefined);
-        doc.layoutKey = deiconify || 'layout';
+        doc.layout_fieldKey = deiconify || 'layout';
     }
     export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: number[]) {
         if (!container) return;
@@ -1431,7 +1432,7 @@ export namespace Doc {
     // based on the modifiers :"check", "x", undefined
     export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
         if (!container) return;
-        const filterField = '_' + (fieldPrefix ? fieldPrefix + '-' : '') + 'docFilters';
+        const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'docFilters';
         const docFilters = Cast(container[filterField], listSpec('string'), []);
         runInAction(() => {
             for (let i = 0; i < docFilters.length; i++) {
@@ -1471,18 +1472,18 @@ export namespace Doc {
     export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
         runInAction(() => {
             if (Doc.NativeWidth(layoutDoc) || Doc.NativeHeight(layoutDoc)) {
-                layoutDoc._viewScale = NumCast(layoutDoc._viewScale, 1) * contentScale;
+                layoutDoc._freeform_scale = NumCast(layoutDoc._freeform_scale, 1) * contentScale;
                 layoutDoc._nativeWidth = undefined;
                 layoutDoc._nativeHeight = undefined;
-                layoutDoc._forceReflow = undefined;
+                layoutDoc._layout_forceReflow = undefined;
                 layoutDoc._nativeHeightUnfrozen = undefined;
                 layoutDoc._nativeDimModifiable = undefined;
             } else {
-                layoutDoc._autoHeight = false;
+                layoutDoc._layout_autoHeight = false;
                 if (!Doc.NativeWidth(layoutDoc)) {
                     layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
                     layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
-                    layoutDoc._forceReflow = true;
+                    layoutDoc._layout_forceReflow = true;
                     layoutDoc._nativeHeightUnfrozen = true;
                     layoutDoc._nativeDimModifiable = true;
                 }
@@ -1710,8 +1711,8 @@ export function IdToDoc(id: string) {
 ScriptingGlobals.add(function idToDoc(id: string): any {
     return IdToDoc(id);
 });
-ScriptingGlobals.add(function renameAlias(doc: any) {
-    return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.aliasNumber})`;
+ScriptingGlobals.add(function renameEmbedding(doc: any) {
+    return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
 });
 ScriptingGlobals.add(function getProto(doc: any) {
     return Doc.GetProto(doc);
@@ -1719,8 +1720,8 @@ ScriptingGlobals.add(function getProto(doc: any) {
 ScriptingGlobals.add(function getDocTemplate(doc?: any) {
     return Doc.getDocTemplate(doc);
 });
-ScriptingGlobals.add(function getAlias(doc: any) {
-    return Doc.MakeAlias(doc);
+ScriptingGlobals.add(function getEmbedding(doc: any) {
+    return Doc.MakeEmbedding(doc);
 });
 ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
     return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto);
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 239b59e83..24cd078f2 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -395,11 +395,11 @@ export namespace RichTextUtils {
                             if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
                                 const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]);
                                 if (linkDoc instanceof Doc) {
-                                    let exported = (await Cast(linkDoc.anchor2, Doc))!;
+                                    let exported = (await Cast(linkDoc.link_anchor_2, Doc))!;
                                     if (!exported.customLayout) {
-                                        exported = Doc.MakeAlias(exported);
+                                        exported = Doc.MakeEmbedding(exported);
                                         DocUtils.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
-                                        linkDoc.anchor2 = exported;
+                                        linkDoc.link_anchor_2 = exported;
                                     }
                                     url = Utils.shareUrl(exported[Id]);
                                 }
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 4cf286a32..251b1149d 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -3,7 +3,7 @@ import { List } from './List';
 import { RefField } from './RefField';
 import { DateField } from './DateField';
 import { ScriptField } from './ScriptField';
-import { URLField, WebField, ImageField } from './URLField';
+import { URLField, WebField, ImageField, CsvField } from './URLField';
 import { TextField } from '@material-ui/core';
 import { RichTextField } from './RichTextField';
 
@@ -104,6 +104,9 @@ export function RTFCast(field: FieldResult) {
 export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
     return Cast(field, ScriptField, defaultVal);
 }
+export function CsvCast(field: FieldResult, defaultVal: CsvField | null = null) {
+    return Cast(field, CsvField, defaultVal);
+}
 export function WebCast(field: FieldResult, defaultVal: WebField | null = null) {
     return Cast(field, WebField, defaultVal);
 }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index b7fd06973..7b2fd74d0 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -13,11 +13,11 @@ export const documentSchema = createSchema({
     links: listSpec(Doc), // computed (readonly) list of links associated with this document
 
     // "Location" properties in a very general sense
-    _curPage: 'number', // current page of a page based document
+    _layout_curPage: 'number', // current page of a page based document
     _currentFrame: 'number', // current frame of a frame based collection (e.g., a progressive slide)
     lastFrame: 'number', // last frame of a frame based collection (e.g., a progressive slide)
     activeFrame: 'number', // the active frame of a frame based animated document
-    _currentTimecode: 'number', // current play back time of a temporal document (video / audio)
+    _layout_currentTimecode: 'number', // current play back time of a temporal document (video / audio)
     _timecodeToShow: 'number', // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
     _timecodeToHIde: 'number', // the time that a document should be hidden
     markers: listSpec(Doc), // list of markers for audio / video
@@ -25,52 +25,52 @@ export const documentSchema = createSchema({
     y: 'number', // y coordinate when in a freeform view
     z: 'number', // z "coordinate" - non-zero specifies the overlay layer of a freeformview
     zIndex: 'number', // zIndex of a document in a freeform view
-    _scrollTop: 'number', // scroll position of a scrollable document (pdf, text, web)
+    _layout_scrollTop: 'number', // scroll position of a scrollable document (pdf, text, web)
     lat: 'number',
     lng: 'number',
 
     // appearance properties on the layout
     '_backgroundGrid-spacing': 'number', // the size of the grid for collection views
-    _autoHeight: 'boolean', // whether the height of the document should be computed automatically based on its contents
+    _layout_autoHeight: 'boolean', // whether the height of the document should be computed automatically based on its contents
     _nativeWidth: 'number', // native width of document which determines how much document contents are scaled when the document's width is set
     _nativeHeight: 'number', // "
     _width: 'number', // width of document in its container's coordinate system
     _height: 'number', // "
-    _xPadding: 'number', // pixels of padding on left/right of collectionfreeformview contents when fitContentsToBox is set
-    _yPadding: 'number', // pixels of padding on top/bottom of collectionfreeformview contents when fitContentsToBox is set
+    _xPadding: 'number', // pixels of padding on left/right of collectionfreeformview contents when freeform_fitContentsToBox is set
+    _yPadding: 'number', // pixels of padding on top/bottom of collectionfreeformview contents when freeform_fitContentsToBox is set
     _xMargin: 'number', // margin added on left/right of most documents to add separation from their container
     _yMargin: 'number', // margin added on top/bottom of most documents to add separation from their container
     _overflow: 'string', // sets overflow behvavior for CollectionFreeForm views
-    _showCaption: 'string', // whether editable caption text is overlayed at the bottom of the document
-    _showTitle: 'string', // the fieldkey(s) whose contents should be displayed at the top of the document. separate multiple keys with ";".  Use :hover suffix to indicate title should be shown on hover
+    _layout_showCaption: 'string', // whether editable caption text is overlayed at the bottom of the document
+    _layout_showTitle: 'string', // the fieldkey(s) whose contents should be displayed at the top of the document. separate multiple keys with ";".  Use :hover suffix to indicate title should be shown on hover
     _pivotField: 'string', // specifies which field key should be used as the timeline/pivot axis
     _columnsFill: 'boolean', // whether documents in a stacking view column should be sized to fill the column
     _columnsSort: 'string', // how a document should be sorted "ascending", "descending", undefined (none)
     _columnsHideIfEmpty: 'boolean', // whether empty stacking view column headings should be hidden
-    _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
-    _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
+    // _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
+    // _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
     _fontSize: 'string',
     _fontFamily: 'string',
-    _sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
+    _layout_sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
 
     // appearance properties on the data document
     backgroundColor: 'string', // background color of document
     borderRounding: 'string', // border radius rounding of document
     boxShadow: 'string', // the amount of shadow around the perimeter of a document
     color: 'string', // foreground color of document
-    fitContentsToBox: 'boolean', // whether freeform view contents should be zoomed/panned to fill the area of the document view box
+    freeform_fitContentsToBox: 'boolean', // whether freeform view contents should be zoomed/panned to fill the area of the document view box
     fontSize: 'string',
     hidden: 'boolean', // whether a document should not be displayed
-    isInkMask: 'boolean', // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
-    layout: 'string', // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
-    layoutKey: 'string', // holds the field key for the field that actually holds the current lyoat
+    stroke_isInkMask: 'boolean', // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
+    layout: 'string', // this is the native layout string for the document.  templates can be added using other fields and setting layout_fieldKey below
+    layout_fieldKey: 'string', // holds the field key for the field that actually holds the current lyoat
     letterSpacing: 'string',
     opacity: 'number', // opacity of document
-    strokeWidth: 'number',
-    strokeBezier: 'number',
-    strokeStartMarker: 'string',
-    strokeEndMarker: 'string',
-    strokeDash: 'string',
+    stroke_width: 'number',
+    stroke_bezier: 'number',
+    stroke_startMarker: 'string',
+    stroke_endMarker: 'string',
+    stroke_dash: 'string',
     textTransform: 'string',
     treeViewOpen: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
     treeViewExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
@@ -87,20 +87,20 @@ export const documentSchema = createSchema({
     followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, )
     hideLinkButton: 'boolean', // whether the blue link counter button should be hidden
     hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
-    linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
+    layout_linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
     isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations
     layers: listSpec('string'), // which layers the document is part of
     _lockedPosition: 'boolean', // whether the document can be moved (dragged)
     _lockedTransform: 'boolean', // whether a freeformview can pan/zoom
-    linkDisplayArrow: 'boolean', // toggles directed arrows
+    layout_linkDisplayArrow: 'boolean', // toggles directed arrows
 
     // drag drop properties
     _stayInCollection: 'boolean', // whether document can be dropped into a different collection
     dragFactory: Doc, // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    dropAction: 'string', // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
-    childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
-    removeDropProperties: listSpec('string'), // properties that should be removed from the alias/copy/etc of this document when it is dropped
+    dropAction: 'string', // override specifying what should happen when this document is dropped (can be "embed", "copy", "move")
+    targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("embed", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
+    childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
+    removeDropProperties: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
 });
 
 export const collectionSchema = createSchema({
diff --git a/src/fields/util.ts b/src/fields/util.ts
index d5b55867e..2a6caaaa3 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -289,7 +289,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         });
 
         // maps over the annotations of the document
-        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).forEach(d => {
+        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
             distributeAcls(key, acl, d, inheritingFromCollection, visited);
             distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited);
         });
@@ -377,7 +377,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
             diff?.op === '$addToSet'
                 ? { $addToSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } }
                 : diff?.op === '$remFromSet'
-                ? { $remFromSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)), hint : diff.hint } }
+                ? { $remFromSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)), hint: diff.hint } }
                 : { $set: { ['fields.' + prop]: SerializationHelper.Serialize(value) } };
         !op.$set && ((op as any).length = diff.length);
         const prevValue = ObjectField.MakeCopy(lastValue as List<any>);
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index a0b437354..c3423e2a5 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -21,7 +21,13 @@ import React = require('react');
 export class AudioUpload extends React.Component {
     @observable public _audioCol: Doc = FieldValue(
         Cast(
-            Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: 'mobile audio', _width: 500, _height: 100 }), Doc) as Doc], { title: 'mobile audio', _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: '0 0' }),
+            Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: 'mobile audio', _width: 500, _height: 100 }), Doc) as Doc], {
+                title: 'mobile audio',
+                _width: 300,
+                _height: 300,
+                _layoutFitContentsToBox: true,
+                boxShadow: '0 0',
+            }),
             Doc
         )
     ) as Doc;
@@ -48,7 +54,7 @@ export class AudioUpload extends React.Component {
                             Doc
                         ) as Doc,
                     ],
-                    { title: 'mobile audio', _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: '0 0' }
+                    { title: 'mobile audio', _width: 300, _height: 300, _layoutFitContentsToBox: true, boxShadow: '0 0' }
                 ),
                 Doc
             )
diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts
index a74e13a62..186f0bcd3 100644
--- a/src/server/ApiManagers/SearchManager.ts
+++ b/src/server/ApiManagers/SearchManager.ts
@@ -1,94 +1,89 @@
-import { exec } from "child_process";
-import { cyan, green, red, yellow } from "colors";
+import { exec } from 'child_process';
+import { cyan, green, red, yellow } from 'colors';
 import * as path from 'path';
-import { log_execution } from "../ActionUtilities";
-import { Database } from "../database";
-import { Method } from "../RouteManager";
-import RouteSubscriber from "../RouteSubscriber";
-import { Search } from "../Search";
-import ApiManager, { Registration } from "./ApiManager";
-import { Directory, pathToDirectory } from "./UploadManager";
+import { log_execution } from '../ActionUtilities';
+import { Database } from '../database';
+import { Method } from '../RouteManager';
+import RouteSubscriber from '../RouteSubscriber';
+import { Search } from '../Search';
+import ApiManager, { Registration } from './ApiManager';
+import { Directory, pathToDirectory } from './UploadManager';
 const findInFiles = require('find-in-files');
 
 export class SearchManager extends ApiManager {
-
     protected initialize(register: Registration): void {
-
         register({
             method: Method.GET,
-            subscription: new RouteSubscriber("solr").add("action"),
+            subscription: new RouteSubscriber('solr').add('action'),
             secureHandler: async ({ req, res }) => {
                 const { action } = req.params;
                 switch (action) {
-                    case "start":
-                    case "stop":
-                        const status = req.params.action === "start";
+                    case 'start':
+                    case 'stop':
+                        const status = req.params.action === 'start';
                         SolrManager.SetRunning(status);
                         break;
-                    case "update":
+                    case 'update':
                         await SolrManager.update();
                         break;
                     default:
                         console.log(yellow(`${action} is an unknown solr operation.`));
                 }
-                res.redirect("/home");
-            }
+                res.redirect('/home');
+            },
         });
 
         register({
             method: Method.GET,
-            subscription: "/textsearch",
+            subscription: '/textsearch',
             secureHandler: async ({ req, res }) => {
                 const q = req.query.q;
                 if (q === undefined) {
                     res.send([]);
                     return;
                 }
-                const resObj: { ids: string[], numFound: number, lines: string[] } = { ids: [], numFound: 0, lines: [] };
+                const resObj: { ids: string[]; numFound: number; lines: string[] } = { ids: [], numFound: 0, lines: [] };
                 let results: any;
                 const dir = pathToDirectory(Directory.text);
                 try {
                     const regex = new RegExp(q.toString());
-                    results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, dir, ".txt$");
+                    results = await findInFiles.find({ term: q, flags: 'ig' }, dir, '.txt$');
                     for (const result in results) {
-                        resObj.ids.push(path.basename(result, ".txt").replace(/upload_/, ""));
+                        resObj.ids.push(path.basename(result, '.txt').replace(/upload_/, ''));
                         resObj.lines.push(results[result].line);
                         resObj.numFound++;
                     }
                     res.send(resObj);
                 } catch (e) {
-                    console.log(red("textsearch:bad RegExp" + q.toString()));
+                    console.log(red('textsearch:bad RegExp' + q.toString()));
                     res.send([]);
                     return;
                 }
-            }
+            },
         });
 
         register({
             method: Method.GET,
-            subscription: "/dashsearch",
+            subscription: '/dashsearch',
             secureHandler: async ({ req, res }) => {
                 const solrQuery: any = {};
-                ["q", "fq", "start", "rows", "sort", "hl.maxAnalyzedChars", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]);
+                ['q', 'fq', 'start', 'rows', 'sort', 'hl.maxAnalyzedChars', 'hl', 'hl.fl'].forEach(key => (solrQuery[key] = req.query[key]));
                 if (solrQuery.q === undefined) {
                     res.send([]);
                     return;
                 }
                 const results = await Search.search(solrQuery);
                 res.send(results);
-            }
+            },
         });
-
     }
-
 }
 
 export namespace SolrManager {
-
     export function SetRunning(status: boolean) {
-        const args = status ? "start" : "stop -p 8983";
+        const args = status ? 'start' : 'stop -p 8983';
         console.log(`solr management: trying to ${args}`);
-        exec(`solr ${args}`, { cwd: "./solr-8.3.1/bin" }, (error, stdout, stderr) => {
+        exec(`solr ${args}`, { cwd: './solr-8.3.1/bin' }, (error, stdout, stderr) => {
             if (error) {
                 console.log(red(`solr management error: unable to ${args} server`));
                 console.log(red(error.message));
@@ -97,39 +92,39 @@ export namespace SolrManager {
             console.log(yellow(stderr));
         });
         if (status) {
-            console.log(cyan("Start script is executing: please allow 15 seconds for solr to start on port 8983."));
+            console.log(cyan('Start script is executing: please allow 15 seconds for solr to start on port 8983.'));
         }
     }
 
     export async function update() {
-        console.log(green("Beginning update..."));
+        console.log(green('Beginning update...'));
         await log_execution<void>({
-            startMessage: "Clearing existing Solr information...",
-            endMessage: "Solr information successfully cleared",
+            startMessage: 'Clearing existing Solr information...',
+            endMessage: 'Solr information successfully cleared',
             action: Search.clear,
-            color: cyan
+            color: cyan,
         });
         const cursor = await log_execution({
-            startMessage: "Connecting to and querying for all documents from database...",
+            startMessage: 'Connecting to and querying for all documents from database...',
             endMessage: ({ result, error }) => {
                 const success = error === null && result !== undefined;
                 if (!success) {
-                    console.log(red("Unable to connect to the database."));
+                    console.log(red('Unable to connect to the database.'));
                     process.exit(0);
                 }
-                return "Connection successful and query complete";
+                return 'Connection successful and query complete';
             },
             action: () => Database.Instance.query({}),
-            color: yellow
+            color: yellow,
         });
         const updates: any[] = [];
         let numDocs = 0;
         function updateDoc(doc: any) {
             numDocs++;
-            if ((numDocs % 50) === 0) {
+            if (numDocs % 50 === 0) {
                 console.log(`Batch of 50 complete, total of ${numDocs}`);
             }
-            if (doc.__type !== "Doc") {
+            if (doc.__type !== 'Doc') {
                 return;
             }
             const fields = doc.fields;
@@ -143,8 +138,8 @@ export namespace SolrManager {
                 const term = ToSearchTerm(value);
                 if (term !== undefined) {
                     const { suffix, value } = term;
-                    if (key.endsWith('lastModified')) {
-                        update["lastModified" + suffix] = value;
+                    if (key.endsWith('modificationDate')) {
+                        update['modificationDate' + suffix] = value;
                     }
                     update[key + suffix] = value;
                     dynfield = true;
@@ -157,51 +152,54 @@ export namespace SolrManager {
         await cursor?.forEach(updateDoc);
         const result = await log_execution({
             startMessage: `Dispatching updates for ${updates.length} documents`,
-            endMessage: "Dispatched updates complete",
+            endMessage: 'Dispatched updates complete',
             action: () => Search.updateDocuments(updates),
-            color: cyan
+            color: cyan,
         });
         try {
             if (result) {
                 const { status } = JSON.parse(result).responseHeader;
-                console.log(status ? red(`Failed with status code (${status})`) : green("Success!"));
+                console.log(status ? red(`Failed with status code (${status})`) : green('Success!'));
             } else {
-                console.log(red("Solr is likely not running!"));
+                console.log(red('Solr is likely not running!'));
             }
         } catch (e) {
-            console.log(red("Error:"));
+            console.log(red('Error:'));
             console.log(e);
-            console.log("\n");
+            console.log('\n');
         }
         await cursor?.close();
     }
 
-    const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = {
-        "number": "_n",
-        "string": "_t",
-        "boolean": "_b",
-        "image": ["_t", "url"],
-        "video": ["_t", "url"],
-        "pdf": ["_t", "url"],
-        "audio": ["_t", "url"],
-        "web": ["_t", "url"],
-        "map": ["_t", "url"],
-        "date": ["_d", value => new Date(value.date).toISOString()],
-        "proxy": ["_i", "fieldId"],
-        "prefetch_proxy": ["_i", "fieldId"],
-        "list": ["_l", list => {
-            const results = [];
-            for (const value of list.fields) {
-                const term = ToSearchTerm(value);
-                if (term) {
-                    results.push(term.value);
+    const suffixMap: { [type: string]: string | [string, string | ((json: any) => any)] } = {
+        number: '_n',
+        string: '_t',
+        boolean: '_b',
+        image: ['_t', 'url'],
+        video: ['_t', 'url'],
+        pdf: ['_t', 'url'],
+        audio: ['_t', 'url'],
+        web: ['_t', 'url'],
+        map: ['_t', 'url'],
+        date: ['_d', value => new Date(value.date).toISOString()],
+        proxy: ['_i', 'fieldId'],
+        prefetch_proxy: ['_i', 'fieldId'],
+        list: [
+            '_l',
+            list => {
+                const results = [];
+                for (const value of list.fields) {
+                    const term = ToSearchTerm(value);
+                    if (term) {
+                        results.push(term.value);
+                    }
                 }
-            }
-            return results.length ? results : null;
-        }]
+                return results.length ? results : null;
+            },
+        ],
     };
 
-    function ToSearchTerm(val: any): { suffix: string, value: any } | undefined {
+    function ToSearchTerm(val: any): { suffix: string; value: any } | undefined {
         if (val === null || val === undefined) {
             return;
         }
@@ -213,7 +211,7 @@ export namespace SolrManager {
 
         if (Array.isArray(suffix)) {
             const accessor = suffix[1];
-            if (typeof accessor === "function") {
+            if (typeof accessor === 'function') {
                 val = accessor(val);
             } else {
                 val = val[accessor];
@@ -223,5 +221,4 @@ export namespace SolrManager {
 
         return { suffix, value: val };
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/server/updateProtos.ts b/src/server/updateProtos.ts
index c5552f6bf..2f3772a77 100644
--- a/src/server/updateProtos.ts
+++ b/src/server/updateProtos.ts
@@ -1,14 +1,22 @@
-import { Database } from "./database";
+import { Database } from './database';
 
-const protos =
-    ["text", "image", "web", "collection", "kvp", "video", "audio", "pdf", "icon", "import", "linkdoc", "map"];
+const protos = ['text', 'image', 'web', 'collection', 'kvp', 'video', 'audio', 'pdf', 'icon', 'import', 'linkdoc', 'map'];
 
 (async function () {
     await Promise.all(
-        protos.map(protoId => new Promise(res => Database.Instance.update(protoId, {
-            $set: { "fields.baseProto": true }
-        }, res)))
+        protos.map(
+            protoId =>
+                new Promise(res =>
+                    Database.Instance.update(
+                        protoId,
+                        {
+                            $set: { 'fields.isBaseProto': true },
+                        },
+                        res
+                    )
+                )
+        )
     );
 
-    console.log("done");
-})();
\ No newline at end of file
+    console.log('done');
+})();
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index f131927f7..be5cdb202 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -476,8 +476,8 @@ export namespace WebSocket {
                 if (term !== undefined) {
                     const { suffix, value } = term;
                     update[key + suffix] = { set: value };
-                    if (key.endsWith('lastModified')) {
-                        update['lastModified' + suffix] = value;
+                    if (key.endsWith('modificationDate')) {
+                        update['modificationDate' + suffix] = value;
                     }
                 }
             }
-- 
cgit v1.2.3-70-g09d2


From 46cf6c823ca8ab628cd8c5bd7fdfe8945344a014 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 16 May 2023 14:50:29 -0400
Subject: fixed bugs with goldenlayout dragging and undoing.  fixed searching
 for filter values in sidebars.  Stopped creating empty list for collections
 when datafield() is accessed because it messes up undo of a collection. fixed
 tab title editing.  from marquee.  Added UndoStack UI and additional naming
 support in code.

---
 src/client/documents/Documents.ts                  | 12 ++--
 src/client/goldenLayout.js                         |  2 +-
 src/client/util/CurrentUserUtils.ts                | 44 +++++++-------
 src/client/util/DragManager.ts                     |  6 +-
 src/client/util/LinkFollower.ts                    |  2 +-
 src/client/util/SelectionManager.ts                |  2 +-
 src/client/util/UndoManager.ts                     | 53 ++++++++++++-----
 src/client/views/ContextMenuItem.tsx               |  5 +-
 src/client/views/DocumentDecorations.tsx           | 46 +++++----------
 src/client/views/EditableView.tsx                  |  2 +
 src/client/views/FilterPanel.tsx                   |  9 ++-
 src/client/views/GlobalKeyHandler.ts               | 10 ++--
 src/client/views/MainView.tsx                      |  8 +--
 src/client/views/PropertiesButtons.tsx             |  6 +-
 .../views/PropertiesDocBacklinksSelector.tsx       |  2 +-
 src/client/views/PropertiesDocContextSelector.tsx  |  2 +-
 src/client/views/PropertiesView.tsx                | 32 +++++-----
 src/client/views/SidebarAnnos.scss                 | 14 +++--
 src/client/views/SidebarAnnos.tsx                  | 15 ++++-
 src/client/views/UndoStack.tsx                     | 30 ++++++++++
 .../views/collections/CollectionDockingView.tsx    | 63 ++++++++++----------
 .../collections/CollectionNoteTakingViewColumn.tsx |  5 +-
 .../collections/CollectionStackedTimeline.tsx      |  3 +-
 .../CollectionStackingViewFieldColumn.tsx          |  4 +-
 src/client/views/collections/CollectionSubView.tsx | 13 ++--
 src/client/views/collections/TabDocView.tsx        | 21 ++++---
 src/client/views/collections/TreeView.tsx          |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 -
 .../collections/collectionFreeForm/MarqueeView.tsx | 26 +++-----
 .../collectionSchema/CollectionSchemaView.tsx      | 32 +++++-----
 .../collectionSchema/SchemaColumnHeader.tsx        |  2 -
 .../collections/collectionSchema/SchemaRowBox.tsx  | 10 ++--
 .../collectionSchema/SchemaTableCell.tsx           |  2 +-
 src/client/views/linking/LinkMenu.tsx              |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 src/client/views/nodes/DocumentLinksButton.tsx     | 20 +++----
 src/client/views/nodes/DocumentView.tsx            | 57 ++++++++++--------
 src/client/views/nodes/LinkDocPreview.tsx          |  3 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 60 ++++++++-----------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 69 +++++++++++-----------
 src/client/views/nodes/trails/PresBox.tsx          |  9 ++-
 src/client/views/nodes/trails/PresElementBox.tsx   | 13 ++--
 src/client/views/pdf/AnchorMenu.tsx                | 11 ++--
 src/client/views/search/SearchBox.tsx              |  2 +
 src/fields/Doc.ts                                  | 18 ++++--
 src/fields/util.ts                                 | 38 +++++++-----
 46 files changed, 421 insertions(+), 372 deletions(-)
 create mode 100644 src/client/views/UndoStack.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2151d0ec7..795d60b48 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -24,7 +24,7 @@ import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox';
 import { FollowLinkScript } from '../util/LinkFollower';
 import { LinkManager } from '../util/LinkManager';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { undoBatch, UndoManager } from '../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionView } from '../views/collections/CollectionView';
@@ -249,7 +249,7 @@ export class DocumentOptions {
     contextMenuIcons?: List<string>;
     defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
     waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
-    dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
+    dontUndo?: boolean; // whether button clicks should be undoable ( true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)
     layout?: string | Doc; // default layout string for a document
     contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
@@ -1532,7 +1532,7 @@ export namespace DocUtils {
                 description: 'Quick Notes',
                 subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({
                     description: ':' + StrCast(note.title),
-                    event: undoBatch((args: { x: number; y: number }) => {
+                    event: undoable((args: { x: number; y: number }) => {
                         const textDoc = Docs.Create.TextDocument('', {
                             _width: 200,
                             x,
@@ -1546,7 +1546,7 @@ export namespace DocUtils {
                             textDoc[pivotField] = pivotValue;
                         }
                         docTextAdder(textDoc);
-                    }),
+                    }, 'create quick note'),
                     icon: StrCast(note.icon) as IconProp,
                 })) as ContextMenuProps[],
                 icon: 'sticky-note',
@@ -1557,7 +1557,7 @@ export namespace DocUtils {
             .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyDataViz)
             .map((dragDoc, i) => ({
                 description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
-                event: undoBatch((args: { x: number; y: number }) => {
+                event: undoable((args: { x: number; y: number }) => {
                     const newDoc = DocUtils.copyDragFactory(dragDoc);
                     if (newDoc) {
                         newDoc.author = Doc.CurrentUserEmail;
@@ -1570,7 +1570,7 @@ export namespace DocUtils {
                         }
                         docAdder?.(newDoc);
                     }
-                }),
+                }, StrCast(dragDoc.title)),
                 icon: Doc.toIcon(dragDoc),
             })) as ContextMenuProps[];
         ContextMenu.Instance.addItem({
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 9cb20d834..843b8bb5f 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -4740,7 +4740,7 @@
                         newstack._$init();
                         newstack.addChild(this.contentItems[0]);
                     }
-                    correctRowOrCol.addChild(newstack,  insertBefore ? 0 : undefined, true);
+                    correctRowOrCol.addChild(newstack,  !insertBefore ? 0 : undefined, true);
                     newstack.config[dimension] = 50;
                     contentItem.config[dimension] = 50;
                     correctRowOrCol.callDownwards('setSize');
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 26fe8f440..b0a2c7d60 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -363,7 +363,7 @@ export class CurrentUserUtils {
         const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
-                title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true,  dontRegisterView: true,
+                title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true,
                 _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
             };
@@ -598,12 +598,12 @@ export class CurrentUserUtils {
                             CurrentUserUtils.createToolButton(opts), scripts, funcs);
         
         const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
-            { scripts: { onClick: "undo()"}, opts: { title: "undo", icon: "undo-alt", toolTip: "Click to undo" }},
-            { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }},
-            { scripts: { },                  opts: { title: "linker", icon: "linkui", toolTip: "link started"}},
-            { scripts: { },                  opts: { title: "currently playing", icon: "currentlyplayingui", toolTip: "currently playing media"}},
+            { scripts: { onClick: "undo()"},      opts: { title: "undo", icon: "undo-alt", toolTip: "Click to undo" }},
+            { scripts: { onClick: "redo()"},      opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }},
+            { scripts: { },                       opts: { title: "linker", icon: "linkui", toolTip: "link started"}},
+            { scripts: { },                       opts: { title: "currently playing", icon: "currentlyplayingui", toolTip: "currently playing media"}},
         ];
-        const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
+        const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
             title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'embed',
             childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
@@ -632,21 +632,21 @@ export class CurrentUserUtils {
     }
     static textTools():Button[] {
         return [
-            { title: "Font",    toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},
+            { title: "Font",     toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
-            { title: "Size",    toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton, width: 75,           toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 },
-            { title: "Color",   toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}},
+            { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton, width: 75,           toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 },
+            { title: "Color",    toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}},
             { title: "Highlight",toolTip:"Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
-            { title: "Bold",    toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Italic",  toolTip: "Italic (Ctrl+I)",      btnType: ButtonType.ToggleButton, icon: "italic",      toolType:"italics",                       scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Under",   toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",   toolType:"underline",                     scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Bullets", toolTip: "Bullet List",          btnType: ButtonType.ToggleButton, icon: "list",        toolType:"bullet",                        scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "#",       toolTip: "Number List",          btnType: ButtonType.ToggleButton, icon: "list-ol",     toolType:"decimal",                       scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Left",    toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
-            { title: "Center",  toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",                        scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Right",   toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right",                         scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Dictate", toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation",                     scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}},
-            { title: "NoLink",  toolTip: "Auto Link",            btnType: ButtonType.ToggleButton, icon: "link",        toolType:"noAutoLink", expertMode:true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
+            { title: "Bold",     toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Italic",   toolTip: "Italic (Ctrl+I)",      btnType: ButtonType.ToggleButton, icon: "italic",      toolType:"italics",                       scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Under",    toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",   toolType:"underline",                     scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Bullets",  toolTip: "Bullet List",          btnType: ButtonType.ToggleButton, icon: "list",        toolType:"bullet",                        scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "#",        toolTip: "Number List",          btnType: ButtonType.ToggleButton, icon: "list-ol",     toolType:"decimal",                       scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Left",     toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
+            { title: "Center",   toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",                        scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Right",    toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right",                         scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Dictate",  toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation",                     scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}},
+            { title: "NoLink",   toolTip: "Auto Link",            btnType: ButtonType.ToggleButton, icon: "link",        toolType:"noAutoLink", expertMode:true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
             // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
             // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}},
             // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", scripts: {onClick:: 'toggleSubscript()'}},
@@ -711,7 +711,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
             backgroundColor: params.backgroundColor ??"transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
-            color: Colors.WHITE, isSystem: true, dontUndo: true, 
+            color: Colors.WHITE, isSystem: true, //dontUndo: true, 
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
             toolType: params.toolType, expertMode: params.expertMode,
@@ -727,14 +727,14 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
             if (!params.subMenu) {
                 return this.setupContextMenuButton(params, menuBtnDoc);
             } else {
-                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
+                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, dontUndo: true,
                     childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
                     linearViewSubMenu: true, linearViewExpandable: true, };
                 const items = params.subMenu?.map(sub =>
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 070e0f918..e3798233e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -263,7 +263,7 @@ export namespace DragManager {
 
     // drags a column from a schema view
     export function StartColumnDrag(ele: HTMLElement[], dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) {
-        StartDrag(ele, dragData, downX, downY, options);
+        StartDrag(ele, dragData, downX, downY, options, undefined, 'Drag Column');
     }
 
     export function SetSnapLines(horizLines: number[], vertLines: number[]) {
@@ -325,10 +325,10 @@ export namespace DragManager {
     export let docsBeingDragged: Doc[] = observable([] as Doc[]);
     export let CanEmbed = false;
     export let DocDragData: DocumentDragData | undefined;
-    export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
+    export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void, dragUndoName?:string) {
         if (dragData.dropAction === 'none') return;
         DocDragData = dragData as DocumentDragData;
-        const batch = UndoManager.StartBatch('dragging');
+        const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag');
         eles = eles.filter(e => e);
         CanEmbed = dragData.canEmbed || false;
         if (!dragDiv) {
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 2812d6c88..f74409e42 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -28,7 +28,7 @@ export class LinkFollower {
     // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
     // depending on the followLinkLocation property of the source (or the link itself as a fallback);
     public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, altKey: boolean) => {
-        const batch = UndoManager.StartBatch('follow link click');
+        const batch = UndoManager.StartBatch('Follow Link');
         runInAction(() => (LinkFollower.IsFollowing = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value
         LinkFollower.traverseLink(
             linkDoc,
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index fba0a4f76..0125331ec 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -118,6 +118,6 @@ ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, exp
     if (type === 'tab') {
         return SelectionManager.Views().lastElement()?.props.renderDepth === 0;
     }
-    let selected = (sel => (checkContext ? DocCast(sel?.context) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
+    let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
     return selected?.type === type || selected?.type_collection === type || !type;
 });
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index d0aec45a6..6fef9d660 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -3,7 +3,7 @@ import { Without } from '../../Utils';
 
 function getBatchName(target: any, key: string | symbol): string {
     const keyName = key.toString();
-    if (target && target.constructor && target.constructor.name) {
+    if (target?.constructor?.name) {
         return `${target.constructor.name}.${keyName}`;
     }
     return keyName;
@@ -34,6 +34,17 @@ function propertyDecorator(target: any, key: string | symbol) {
     });
 }
 
+export function undoable(fn: (...args: any[]) => any, batchName: string): (...args: any[]) => any {
+    return function () {
+        const batch = UndoManager.StartBatch(batchName);
+        try {
+            return fn.apply(undefined, arguments as any);
+        } finally {
+            batch.end();
+        }
+    };
+}
+
 export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any;
 export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any;
 export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
@@ -73,15 +84,18 @@ export namespace UndoManager {
     }
     type UndoBatch = UndoEvent[];
 
+    export let undoStackNames: string[] = observable([]);
+    export let redoStackNames: string[] = observable([]);
     export let undoStack: UndoBatch[] = observable([]);
     export let redoStack: UndoBatch[] = observable([]);
     let currentBatch: UndoBatch | undefined;
-    export let batchCounter = 0;
+    export let batchCounter = observable.box(0);
     let undoing = false;
     let tempEvents: UndoEvent[] | undefined = undefined;
 
-    export function AddEvent(event: UndoEvent): void {
-        if (currentBatch && batchCounter && !undoing) {
+    export function AddEvent(event: UndoEvent, value?: any): void {
+        if (currentBatch && batchCounter.get() && !undoing) {
+            console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + value);
             currentBatch.push(event);
             tempEvents?.push(event);
         }
@@ -135,11 +149,13 @@ export namespace UndoManager {
 
         private dispose = (cancel: boolean) => {
             if (this.disposed) {
-                throw new Error('Cannot dispose an already disposed batch');
+                console.log('WARNING: undo batch already disposed');
+                return false;
+            } else {
+                this.disposed = true;
+                openBatches.splice(openBatches.indexOf(this));
+                return EndBatch(this.batchName, cancel);
             }
-            this.disposed = true;
-            openBatches.splice(openBatches.indexOf(this));
-            return EndBatch(cancel);
         };
 
         end = () => this.dispose(false);
@@ -147,22 +163,23 @@ export namespace UndoManager {
     }
 
     export function StartBatch(batchName: string): Batch {
-        // console.log("Start " + batchCounter + " " + batchName);
-        batchCounter++;
-        if (batchCounter > 0 && currentBatch === undefined) {
+        console.log('                          '.slice(0, batchCounter.get()) + 'Start ' + batchCounter + ' ' + batchName);
+        runInAction(() => batchCounter.set(batchCounter.get() + 1));
+        if (currentBatch === undefined) {
             currentBatch = [];
         }
         return new Batch(batchName);
     }
 
-    const EndBatch = action((cancel: boolean = false) => {
-        batchCounter--;
-        // console.log("End " + batchCounter);
-        if (batchCounter === 0 && currentBatch?.length) {
-            // console.log("------ended----")
+    const EndBatch = action((batchName: string, cancel: boolean = false) => {
+        runInAction(() => batchCounter.set(batchCounter.get() - 1));
+        console.log('                          '.slice(0, batchCounter.get()) + 'End ' + batchName + ' (' + currentBatch?.length + ')');
+        if (batchCounter.get() === 0 && currentBatch?.length) {
             if (!cancel) {
                 undoStack.push(currentBatch);
+                undoStackNames.push(batchName ?? '???');
             }
+            redoStackNames.length = 0;
             redoStack.length = 0;
             currentBatch = undefined;
             return true;
@@ -204,6 +221,7 @@ export namespace UndoManager {
             return;
         }
 
+        const names = undoStackNames.pop();
         const commands = undoStack.pop();
         if (!commands) {
             return;
@@ -215,6 +233,7 @@ export namespace UndoManager {
         }
         undoing = false;
 
+        redoStackNames.push(names ?? '???');
         redoStack.push(commands);
     });
 
@@ -223,6 +242,7 @@ export namespace UndoManager {
             return;
         }
 
+        const names = redoStackNames.pop();
         const commands = redoStack.pop();
         if (!commands) {
             return;
@@ -234,6 +254,7 @@ export namespace UndoManager {
         }
         undoing = false;
 
+        undoStackNames.push(names ?? '???');
         undoStack.push(commands);
     });
 }
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index e87d2046b..33f250986 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -40,10 +40,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
     handleEvent = async (e: React.MouseEvent<HTMLDivElement>) => {
         if ('event' in this.props) {
             this.props.closeMenu?.();
-            let batch: UndoManager.Batch | undefined;
-            if (this.props.undoable !== false) {
-                batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`);
-            }
+            const batch = this.props.undoable !== false ? UndoManager.StartBatch(`Click Menu item: ${this.props.description}`) : undefined;
             await this.props.event({ x: e.clientX, y: e.clientY });
             batch?.end();
         }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 39073d763..8077b9af1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -2,23 +2,25 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { IconButton } from 'browndash-components';
-import { action, computed, observable, reaction, runInAction } from 'mobx';
+import { action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { DateField } from '../../fields/DateField';
 import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
-import { Document } from '../../fields/documentSchemas';
 import { InkField } from '../../fields/InkField';
+import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
 import { GetEffectiveAcl } from '../../fields/util';
 import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
+import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
+import { LinkFollower } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
 import { SnappingManager } from '../util/SnappingManager';
-import { undoBatch, UndoManager } from '../util/UndoManager';
+import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentButtonBar } from './DocumentButtonBar';
@@ -27,15 +29,11 @@ import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
-import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
+import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
-import { RichTextField } from '../../fields/RichTextField';
-import { LinkFollower } from '../util/LinkFollower';
 import _ = require('lodash');
-import { DocumentManager } from '../util/DocumentManager';
-import { isUndefined } from 'lodash';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -150,7 +148,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
                         }
                     }),
-                'title blur'
+                'edit title'
             );
         }
     };
@@ -227,11 +225,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             .filter(v => v && v.props.renderDepth > 0);
         if (forceDeleteOrIconify === false && this._iconifyBatch) return;
         this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
-        if (!this._iconifyBatch) {
-            this._iconifyBatch = UndoManager.StartBatch('iconifying');
-        } else {
-            forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
-        }
         var iconifyingCount = views.length;
         const finished = action((force?: boolean) => {
             if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
@@ -250,6 +243,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 this._iconifyBatch = undefined;
             }
         });
+        if (!this._iconifyBatch) {
+            this._iconifyBatch = UndoManager.StartBatch(forceDeleteOrIconify ? 'delete selected docs' : 'iconifying');
+        } else {
+            forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
+        }
+
         if (forceDeleteOrIconify) finished(forceDeleteOrIconify);
         else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
     };
@@ -397,7 +396,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     onRotateDown = (e: React.PointerEvent): void => {
         this._isRotating = true;
         const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] };
-        const rotateUndo = UndoManager.StartBatch('rotatedown');
+        const rotateUndo = UndoManager.StartBatch('drag rotation');
         const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
         const centerPoint = this.rotCenter.slice();
         const infos = new Map<Doc, { unrotatedDocPos: { x: number; y: number }; startRotCtr: { x: number; y: number }; accumRot: number }>();
@@ -465,7 +464,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const bounds = e.currentTarget.getBoundingClientRect();
         this._offX = this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX;
         this._offY = this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY;
-        this._resizeUndo = UndoManager.StartBatch('DocDecs resize');
+        this._resizeUndo = UndoManager.StartBatch('drag resizing');
         this._snapX = e.pageX;
         this._snapY = e.pageY;
         const ffviewSet = new Set<CollectionFreeFormView>();
@@ -770,20 +769,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
-                <div
-                    className={`documentDecorations-${key}Button`}
-                    onContextMenu={e => e.preventDefault()}
-                    onPointerDown={
-                        pointerDown ??
-                        (e =>
-                            setupMoveUpEvents(
-                                this,
-                                e,
-                                returnFalse,
-                                emptyFunction,
-                                undoBatch(e => click!(e))
-                            ))
-                    }>
+                <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => click!(e)))}>
                     <FontAwesomeIcon icon={icon as any} />
                 </div>
             </Tooltip>
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 6b4132814..7043edcee 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -156,12 +156,14 @@ export class EditableView extends React.Component<EditableProps> {
                 break;
             case ':':
                 if (this.props.menuCallback) {
+                    e.stopPropagation();
                     this.props.menuCallback(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
                     break;
                 }
 
             default:
                 if (this.props.textCallback?.(e.key)) {
+                    e.stopPropagation();
                     this._editing = false;
                     this.props.isEditingCallback?.(false);
                 }
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 75e0e7c4c..53c1f1018 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -10,6 +10,7 @@ import { UserOptions } from '../util/GroupManager';
 import './FilterPanel.scss';
 import { FieldView } from './nodes/FieldView';
 import { SearchBox } from './search/SearchBox';
+import { undoable } from '../util/UndoManager';
 
 interface filterProps {
     rootDoc: Doc;
@@ -31,7 +32,7 @@ export class FilterPanel extends React.Component<filterProps> {
         return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data';
     }
     @computed get targetDocChildren() {
-        return DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data);
+        return [...DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.targetDoc[Doc.LayoutFieldKey(this.targetDoc) + '_sidebar'])];
     }
 
     @computed get allDocs() {
@@ -89,6 +90,7 @@ export class FilterPanel extends React.Component<filterProps> {
                     const fieldKey = Doc.LayoutFieldKey(t);
                     const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
                     DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
+                    annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
                 });
                 subDocs = newarray;
             }
@@ -204,7 +206,8 @@ export class FilterPanel extends React.Component<filterProps> {
                                 .find(filter => filter.split(':')[0] === facetHeader)
                                 ?.split(':')[1] ?? '-empty-'
                         }
-                        onKeyDown={e => e.key === 'Enter' && Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match')}
+                        onBlur={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')}
+                        onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)}
                     />
                 );
             case 'checkbox':
@@ -220,7 +223,7 @@ export class FilterPanel extends React.Component<filterProps> {
                                         ?.split(':')[2] === 'check'
                                 }
                                 type={type}
-                                onChange={e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove')}
+                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')}
                             />
                             {facetValue}
                         </div>
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index b9b92dd2b..625bc760d 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -161,12 +161,10 @@ export class KeyManager {
             case 'delete':
             case 'backspace':
                 if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
-                    UndoManager.RunInBatch(() => {
-                        if (LightboxView.LightboxDoc) {
-                            LightboxView.SetLightboxDoc(undefined);
-                            SelectionManager.DeselectAll();
-                        } else DocumentDecorations.Instance.onCloseClick(true);
-                    }, 'backspace');
+                    if (LightboxView.LightboxDoc) {
+                        LightboxView.SetLightboxDoc(undefined);
+                        SelectionManager.DeselectAll();
+                    } else DocumentDecorations.Instance.onCloseClick(true);
                     return { stopPropagation: true, preventDefault: true };
                 }
                 break;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 853f9cace..50f451c0a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -915,11 +915,11 @@ export class MainView extends React.Component {
         return !SelectionManager.Views().some(dv => dv.rootDoc.freeform_snapLines) ? null : (
             <div className="mainView-snapLines">
                 <svg style={{ width: '100%', height: '100%' }}>
-                    {SnappingManager.horizSnapLines().map(l => (
-                        <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
+                    {SnappingManager.horizSnapLines().map((l, i) => (
+                        <line key={i} x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
                     ))}
-                    {SnappingManager.vertSnapLines().map(l => (
-                        <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
+                    {SnappingManager.vertSnapLines().map((l, i) => (
+                        <line key={i} y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
                     ))}
                 </svg>
             </div>
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index a37447505..a5c58c9d2 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -14,7 +14,7 @@ import { DocUtils } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
-import { undoBatch } from '../util/UndoManager';
+import { undoable, undoBatch } from '../util/UndoManager';
 import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { DocumentView, OpenWhere } from './nodes/DocumentView';
@@ -51,11 +51,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     <div
                         className={`propertiesButtons-linkButton-empty toggle-${StrCast(targetDoc[property]).includes(':hover') ? 'hover' : targetDoc[property] ? 'on' : 'off'}`}
                         onPointerDown={e => e.stopPropagation()}
-                        onClick={undoBatch(() => {
+                        onClick={undoable(() => {
                             if (SelectionManager.Views().length > 1) {
                                 SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
                             } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
-                        })}>
+                        }, property)}>
                         <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} />
                     </div>
                     <div className="propertiesButtons-title">{label}</div>
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 46e6fd188..7b21629da 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -25,7 +25,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
 
         if (otherdoc) {
             otherdoc.hidden = false;
-            this.props.addDocTab(Doc.IsDocDataProto(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, OpenWhere.toggleRight);
+            this.props.addDocTab(Doc.IsDataProto(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, OpenWhere.toggleRight);
         }
     };
 
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 12a09196e..e1279c9a7 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -48,7 +48,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
 
     getOnClick = (col: Doc, target: Doc) => {
         if (!this.props.DocView) return;
-        col = Doc.IsDocDataProto(col) ? Doc.MakeDelegate(col) : col;
+        col = Doc.IsDataProto(col) ? Doc.MakeDelegate(col) : col;
         DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col);
     };
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 19c138a21..297820e37 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -21,7 +21,7 @@ import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SharingManager } from '../util/SharingManager';
 import { Transform } from '../util/Transform';
-import { undoBatch, UndoManager } from '../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
 import { EditableView } from './EditableView';
 import { FilterPanel } from './FilterPanel';
 import { Colors } from './global/globalEnums';
@@ -538,6 +538,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     className="inputBox-input"
                     type="text"
                     value={value}
+                    readOnly={true}
                     onChange={e => {
                         setter(e.target.value);
                     }}
@@ -656,21 +657,21 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return this.inputBoxDuo(
             'hgt',
             this.shapeHgt,
-            (val: string) => {
+            undoable((val: string) => {
                 if (!isNaN(Number(val))) {
                     this.shapeHgt = val;
                 }
                 return true;
-            },
+            }, 'set height'),
             'H:',
             'wid',
             this.shapeWid,
-            (val: string) => {
+            undoable((val: string) => {
                 if (!isNaN(Number(val))) {
                     this.shapeWid = val;
                 }
                 return true;
-            },
+            }, 'set width'),
             'W:'
         );
     }
@@ -678,21 +679,21 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return this.inputBoxDuo(
             'Xps',
             this.shapeXps,
-            (val: string) => {
+            undoable((val: string) => {
                 if (val !== '0' && !isNaN(Number(val))) {
                     this.shapeXps = val;
                 }
                 return true;
-            },
+            }, 'set x coord'),
             'X:',
             'Yps',
             this.shapeYps,
-            (val: string) => {
+            undoable((val: string) => {
                 if (val !== '0' && !isNaN(Number(val))) {
                     this.shapeYps = val;
                 }
                 return true;
-            },
+            }, 'set y coord'),
             'Y:'
         );
     }
@@ -867,7 +868,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     regInput = (key: string, value: any, setter: (val: string) => {}) => {
         return (
             <div className="inputBox">
-                <input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} />
+                <input className="inputBox-input" type="text" readOnly={true} value={value} onChange={e => setter(e.target.value)} />
                 <div className="inputBox-button">
                     <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
                         <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
@@ -1228,8 +1229,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         }
     });
 
-    @undoBatch
-    changeFollowBehavior = action((follow: Opt<string>) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = follow));
+    changeFollowBehavior = undoable((loc: Opt<string>) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = loc), 'change follow behavior');
 
     @undoBatch
     changeAnimationBehavior = action((behavior: string) => this.sourceAnchor && (this.sourceAnchor.followLinkAnimEffect = behavior));
@@ -1315,6 +1315,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 autoComplete={'off'}
                 id="link_relationship_input"
                 value={StrCast(LinkManager.currentLink?.link_relationship)}
+                readOnly={true}
                 onKeyDown={this.onRelationshipKey}
                 onBlur={this.onSelectOutRelationship}
                 onChange={e => this.handlelinkRelationshipChange(e.currentTarget.value)}
@@ -1332,6 +1333,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 style={{ textAlign: 'left' }}
                 id="link_description_input"
                 value={Field.toString(LinkManager.currentLink?.link_description as any as Field)}
+                readOnly={true}
                 onKeyDown={this.onDescriptionKey}
                 onBlur={this.onSelectOutDesc}
                 onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
@@ -1457,7 +1459,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                             <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
                                                 <option value="default">Default</option>
                                                 {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
-                                                    <option value={effect.toString()}>{effect.toString()}</option>
+                                                    <option key={effect.toString()} value={effect.toString()}>
+                                                        {effect.toString()}
+                                                    </option>
                                                 ))}
                                             </select>
                                             <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
@@ -1567,7 +1571,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                         <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
                                             <p>Zoom %</p>
                                             <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
-                                                <input className="presBox-input" style={{ width: '100%' }} type="number" value={zoom} />
+                                                <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
                                                 <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
                                                     <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
                                                         <FontAwesomeIcon icon={'caret-up'} />
diff --git a/src/client/views/SidebarAnnos.scss b/src/client/views/SidebarAnnos.scss
index a0506cb3a..d7de2b641 100644
--- a/src/client/views/SidebarAnnos.scss
+++ b/src/client/views/SidebarAnnos.scss
@@ -4,22 +4,26 @@
     overflow: auto;
     flex-flow: row;
     flex-wrap: wrap;
-    .sidebarAnnos-filterTag, .sidebarAnnos-filterTag-active,
-    .sidebarAnnos-filterUser, .sidebarAnnos-filterUser-active {
+    .sidebarAnnos-filterTag,
+    .sidebarAnnos-filterTag-active,
+    .sidebarAnnos-filterUser,
+    .sidebarAnnos-filterUser-active {
         font-weight: bold;
         font-size: 10px;
         padding-left: 5;
         padding-right: 5;
         box-shadow: black 1px 1px 3px;
         border-radius: 5;
-        margin: 2; 
+        margin: 2;
         height: 15;
         background-color: lightgrey;
     }
-    .sidebarAnnos-filterUser, .sidebarAnnos-filterUser-active {
+    .sidebarAnnos-filterUser,
+    .sidebarAnnos-filterUser-active {
         border-radius: 15px;
     }
+    .sidebarAnnos-filterUser-active,
     .sidebarAnnos-filterTag-active {
         background-color: white;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index e12621f35..c9e52a1db 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -49,13 +49,21 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         );
         return keys;
     }
+    @computed get allHashtags() {
+        const keys = new Set<string>();
+        DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag)));
+        return Array.from(keys.keys())
+            .filter(key => key[0])
+            .filter(key => !key.startsWith('_') && (key[0] === '#' || key[0] === key[0].toUpperCase()))
+            .sort();
+    }
     @computed get allUsers() {
         const keys = new Set<string>();
         DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
         return Array.from(keys.keys()).sort();
     }
     get filtersKey() {
-        return '_' + this.sidebarKey + '-docFilters';
+        return '_' + this.sidebarKey + '_docFilters';
     }
 
     anchorMenuClick = (anchor: Doc) => {
@@ -179,9 +187,9 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     };
     render() {
         const renderTag = (tag: string) => {
-            const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
+            const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags:${tag}:check`);
             return (
-                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, 'check', true, this.sidebarKey, e.shiftKey)}>
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, 'tags', tag, 'check', true, this.sidebarKey, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -216,6 +224,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 }}>
                 <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight() }} onWheel={e => e.stopPropagation()}>
                     {this.allUsers.map(renderUsers)}
+                    {this.allHashtags.map(renderTag)}
                     {Array.from(this.allMetadata.keys())
                         .sort()
                         .map(key => renderMeta(key, this.allMetadata.get(key)))}
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
new file mode 100644
index 000000000..01e184d6b
--- /dev/null
+++ b/src/client/views/UndoStack.tsx
@@ -0,0 +1,30 @@
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { UndoManager } from '../util/UndoManager';
+import './ScriptingRepl.scss';
+
+@observer
+export class UndoStack extends React.Component {
+    render() {
+        return (
+            <div className="scriptingRepl-outerContainer">
+                <div className="scriptingRepl-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })} style={{ background: UndoManager.batchCounter.get() ? 'yellow' : undefined }}>
+                    {UndoManager.undoStackNames.map((name, i) => (
+                        <div className="scriptingRepl-resultContainer" key={i}>
+                            <div className="scriptingRepl-commandString">{name.replace(/[^\.]*\./, '')}</div>
+                        </div>
+                    ))}
+                    {Array.from(UndoManager.redoStackNames)
+                        .reverse()
+                        .map((name, i) => (
+                            <div className="scriptingRepl-resultContainer" key={i}>
+                                <div className="scriptingRepl-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
+                                    {name.replace(/[^\.]*\./, '')}
+                                </div>
+                            </div>
+                        ))}
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cce21a3aa..4ae24af60 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -6,7 +6,7 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
-import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { inheritParentAcls } from '../../../fields/util';
 import { emptyFunction, incrementTitleCopy } from '../../../Utils';
@@ -20,14 +20,14 @@ import { SelectionManager } from '../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { DashboardView } from '../DashboardView';
 import { LightboxView } from '../LightboxView';
+import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
+import { OverlayView } from '../OverlayView';
+import { ScriptingRepl } from '../ScriptingRepl';
 import './CollectionDockingView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
 import React = require('react');
-import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
-import { OverlayView } from '../OverlayView';
-import { ScriptingRepl } from '../ScriptingRepl';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -85,6 +85,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
     tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => {
+        this._flush = this._flush ?? UndoManager.StartBatch('tab move');
         const dashDoc = proxy?._contentItem?.tab?.DashDoc as Doc;
         dashDoc && (DragManager.DocDragData = new DragManager.DocumentDragData([proxy._contentItem.tab.DashDoc]));
         DragManager.CompleteWindowDrag = (aborted: boolean) => {
@@ -92,12 +93,12 @@ export class CollectionDockingView extends CollectionSubView() {
                 proxy._dragListener.AbortDrag();
                 if (this._flush) {
                     this._flush.cancel(); // cancel the undo change being logged
-                    this._flush = undefined;
                     this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout)
                 }
                 DragManager.CompleteWindowDrag = undefined;
             }
             finishDrag?.(aborted);
+            setTimeout(this.endUndoBatch, 100);
         };
     };
     @undoBatch
@@ -180,7 +181,6 @@ export class CollectionDockingView extends CollectionSubView() {
     //
     //  Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side
     //
-    @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
         if (document?._type_collection === CollectionViewType.Docking) return DashboardView.openDashboard(document);
@@ -195,6 +195,8 @@ export class CollectionDockingView extends CollectionSubView() {
         if (!instance) return false;
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue);
 
+        CollectionDockingView.Instance._flush = CollectionDockingView.Instance._flush ?? UndoManager.StartBatch('Add Split');
+        setTimeout(CollectionDockingView.Instance.endUndoBatch, 100);
         if (!pullSide && stack) {
             stack.addChild(docContentConfig, undefined);
             setTimeout(() => stack.setActiveContentItem(stack.contentItems[stack.contentItems.length - 1]));
@@ -370,16 +372,30 @@ export class CollectionDockingView extends CollectionSubView() {
         !LightboxView.LightboxDoc && cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height);
     };
 
+    endUndoBatch = () => {
+        const json = JSON.stringify(this._goldenLayout.toConfig());
+        const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+        const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', ''));
+        const docs = !docids
+            ? []
+            : docids
+                  .map(id => DocServer.GetCachedRefField(id))
+                  .filter(f => f)
+                  .map(f => f as Doc);
+        const changesMade = this.props.Document.dockingConfig !== json;
+        if (changesMade) {
+            this.props.Document.dockingConfig = json;
+            this.props.Document.data = new List<Doc>(docs);
+        }
+        this._flush?.end();
+        this._flush = undefined;
+    };
+
     @action
     onPointerUp = (e: MouseEvent): void => {
         window.removeEventListener('pointerup', this.onPointerUp);
-        const flush = this._flush;
-        this._flush = undefined;
-        if (flush) {
-            DragManager.CompleteWindowDrag = undefined;
-            if (!this.stateChanged()) flush.cancel();
-            else flush.end();
-        }
+        DragManager.CompleteWindowDrag = undefined;
+        setTimeout(this.endUndoBatch, 100);
     };
 
     @action
@@ -393,10 +409,8 @@ export class CollectionDockingView extends CollectionSubView() {
             window.addEventListener('mouseup', this.onPointerUp);
             if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) {
                 const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : '';
-                if (!className.includes('lm_close') && !className.includes('lm_maximise')) {
-                    this._flush = UndoManager.StartBatch('golden layout edit');
-                    DocServer.UPDATE_SERVER_CACHE();
-                }
+                if (className.includes('lm_maximise')) this._flush = UndoManager.StartBatch('tab maximize');
+                else if (!className.includes('lm_close')) DocServer.UPDATE_SERVER_CACHE();
             }
         }
         if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
@@ -452,25 +466,12 @@ export class CollectionDockingView extends CollectionSubView() {
     stateChanged = () => {
         this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         const json = JSON.stringify(this._goldenLayout.toConfig());
-        const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
-        const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', ''));
-        const docs = !docids
-            ? []
-            : docids
-                  .map(id => DocServer.GetCachedRefField(id))
-                  .filter(f => f)
-                  .map(f => f as Doc);
         const changesMade = this.props.Document.dockingConfig !== json;
-        if (changesMade && !this._flush) {
-            UndoManager.RunInBatch(() => {
-                this.props.Document.dockingConfig = json;
-                this.props.Document.data = new List<Doc>(docs);
-            }, 'state changed');
-        }
         return changesMade;
     };
 
     tabDestroyed = (tab: any) => {
+        this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
         if (tab.DashDoc && ![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) {
             Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
             Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 63becac1e..2f28ecd00 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -121,7 +121,8 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
 
     @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = '#b4b4b4');
     @action pointerLeave = () => (this._background = 'inherit');
-    textCallback = (char: string) => this.addNewTextDoc('-typed text-', false, true);
+    @undoBatch
+    addTextNote = (char: string) => this.addNewTextDoc('-typed text-', false, true);
 
     // addNewTextDoc is called when a user starts typing in a column to create a new node
     @action
@@ -272,7 +273,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                         {!this.props.chromeHidden ? (
                             <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
                                 <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
-                                    <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
+                                    <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
                                 </div>
                                 <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
                                     <EditableView {...this.props.editableViewProps()} />
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 6b4c8a3e9..b131d38d8 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -119,7 +119,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         // onClick play scripts
         CollectionStackedTimeline.RangeScript =
             CollectionStackedTimeline.RangeScript ||
-            ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
+            ScriptField.MakeFunction(`setTimeout(() => scriptContext.clickAnchor(this, clientX))`, {
+                // setTimeout is a hack to run script in its own properly named undo group (instead of being part of the generic onClick)
                 self: Doc.name,
                 scriptContext: 'any',
                 clientX: 'number',
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 243550c0b..6be9cb72d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -128,7 +128,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = '#b4b4b4');
     @action pointerLeave = () => (this._background = 'inherit');
-    textCallback = (char: string) => this.addNewTextDoc('-typed text-', false, true);
+    @undoBatch typedNote = (char: string) => this.addNewTextDoc('-typed text-', false, true);
 
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
@@ -363,7 +363,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                                 <EditableView
                                     GetValue={returnEmptyString}
                                     SetValue={this.addNewTextDoc}
-                                    textCallback={this.textCallback}
+                                    textCallback={this.typedNote}
                                     placeholder={"Type ':' for commands"}
                                     contents={<FontAwesomeIcon icon={'plus'} />}
                                     menuCallback={this.menuCallback}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bcda13c8b..cfe78afa1 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -5,7 +5,6 @@ import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from '../../../
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
 import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -65,11 +64,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         // 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'
         @computed get dataField() {
-            if (this.layoutDoc[this.props.fieldKey]) return this.layoutDoc[this.props.fieldKey];
-            // sets the dataDoc's data field to an empty list if the data field is undefined - prevents issues with addonly
-            // setTimeout changes it outside of the @computed section
-            !this.dataDoc[this.props.fieldKey] && setTimeout(() => !this.dataDoc[this.props.fieldKey] && (this.dataDoc[this.props.fieldKey] = new List<Doc>()));
-            return this.dataDoc[this.props.fieldKey];
+            return this.layoutDoc[this.props.fieldKey];
         }
 
         get childLayoutPairs(): { layout: Doc; data: Doc }[] {
@@ -130,8 +125,9 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const fieldKey = Doc.LayoutFieldKey(d);
                     const annos = !Field.toString(Doc.LayoutField(d) as Field).includes(CollectionView.name);
                     const data = d[annos ? fieldKey + '_annotations' : fieldKey];
-                    if (data !== undefined) {
-                        let subDocs = DocListCast(data);
+                    const side = annos && d[fieldKey + '_sidebar'];
+                    if (data !== undefined || side !== undefined) {
+                        let subDocs = [...DocListCast(data), ...DocListCast(side)];
                         if (subDocs.length > 0) {
                             let newarray: Doc[] = [];
                             notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, d).length);
@@ -142,6 +138,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                     const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name);
                                     notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, d).length));
                                     DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
+                                    annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
                                 });
                                 subDocs = newarray;
                             }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 7e88959a4..33fa434e1 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -13,14 +13,13 @@ import { listSpec } from '../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
-import { DocUtils } from '../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoable, UndoManager } from '../../util/UndoManager';
 import { DashboardView } from '../DashboardView';
 import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
@@ -116,15 +115,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
             titleEle.size = StrCast(doc.title).length + 3;
             titleEle.value = doc.title;
-            titleEle.onkeydown = (e: KeyboardEvent) => {
-                e.stopPropagation();
-            };
-            titleEle.onchange = undoBatch(
-                action((e: any) => {
+            titleEle.onkeydown = (e: KeyboardEvent) => e.stopPropagation();
+            titleEle.onchange = (e: any) => {
+                undoable(() => {
                     titleEle.size = e.currentTarget.value.length + 3;
                     Doc.GetProto(doc).title = e.currentTarget.value;
-                })
-            );
+                }, 'edit tab title')();
+            };
 
             if (tab.element[0].children[1].children.length === 1) {
                 iconWrap.className = 'lm_iconWrap lm_moreInfo';
@@ -198,7 +195,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 action(selected => {
                     if (selected) this._activated = true;
                     const toggle = tab.element[0].children[2].children[0] as HTMLInputElement;
-                    selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch');
+                    if (selected && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
+                        undoable(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch')();
+                    }
                     toggle.style.fontWeight = selected ? 'bold' : '';
                     // toggle.style.textTransform = selected ? "uppercase" : "";
                 }),
@@ -234,7 +233,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     public static PinDoc(docs: Doc | Doc[], pinProps: PinProps) {
         const docList = docs instanceof Doc ? [docs] : docs;
 
-        const batch = UndoManager.StartBatch('pinning doc');
+        const batch = UndoManager.StartBatch('Pin doc to pres trail');
         const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true);
 
         if (!Doc.ActivePresentation) {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 037148bb9..f56eaee07 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -102,7 +102,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _treedropDisposer?: DragManager.DragDropDisposer;
 
     get treeViewOpenIsTransient() {
-        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDocDataProto(this.doc);
+        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDataProto(this.doc);
     }
     set treeViewOpen(c: boolean) {
         if (this.treeViewOpenIsTransient) this._transientOpenState = c;
@@ -221,7 +221,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         } else {
             // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns,  (2) has no context field ... otherwise make a new embedding
             const bestEmbedding =
-                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDocDataProto(docView.props.Document)
+                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document)
                     ? docView.props.Document
                     : DocListCast(this.props.document.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail);
             const nextBestEmbedding = DocListCast(this.props.document.proto_embeddings).find(doc => doc.author === Doc.CurrentUserEmail);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5ac444147..95046661e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -483,7 +483,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return false;
     }
 
-    @undoBatch
     @action
     updateClusters(_freeform_useClusters: boolean) {
         this.props.Document._freeform_useClusters = _freeform_useClusters;
@@ -534,7 +533,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     }
 
-    @undoBatch
     @action
     updateCluster(doc: Doc) {
         const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 47d7801e6..641088675 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -102,7 +102,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this._lassoPts = [];
     };
 
-    @undoBatch
     @action
     onKeyPress = (e: KeyboardEvent) => {
         //make textbox and add it to this collection
@@ -111,7 +110,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
         if (e.key === '?') {
             cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
-
             cm.displayMenu(this._downX, this._downY, undefined, true);
             e.stopPropagation();
         } else if (e.key === 'u' && this.props.ungroup) {
@@ -173,7 +171,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             e.stopPropagation();
         } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
             FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : '';
-            FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('live text batch');
+            FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note');
             this.props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this.props.xPadding === 0));
             e.stopPropagation();
         }
@@ -396,6 +394,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         return newCollection;
     });
 
+    @undoBatch
     @action
     pileup = (e: KeyboardEvent | React.PointerEvent | undefined) => {
         const selected = this.marqueeSelect(false);
@@ -508,8 +507,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     };
 
     @undoBatch
-    @action
-    summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+    summary = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
         const selected = this.marqueeSelect(false).map(d => {
             this.props.removeDocument?.(d);
             d.x = NumCast(d.x) - this.Bounds.left;
@@ -534,19 +532,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.props.addDocument?.(portal);
         this.props.addLiveTextDocument(summary);
         MarqueeOptionsMenu.Instance.fadeOut(true);
-    };
+    });
 
     @action
-    background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const newCollection = this.getCollection([], undefined, undefined);
-        this.props.addDocument?.(newCollection);
-        MarqueeOptionsMenu.Instance.fadeOut(true);
-        this.hideMarquee();
-        setTimeout(() => this.props.selectDocuments([newCollection]));
-    };
-
-    @undoBatch
-    marqueeCommand = action((e: KeyboardEvent) => {
+    marqueeCommand = (e: KeyboardEvent) => {
         if (this._commandExecuted || (e as any).propagationIsStopped) {
             return;
         }
@@ -557,7 +546,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             this.delete(e, e.key === 'h');
             e.stopPropagation();
         }
-        if ('cbtsSpg'.indexOf(e.key) !== -1) {
+        if ('ctsSpg'.indexOf(e.key) !== -1) {
             this._commandExecuted = true;
             e.stopPropagation();
             e.preventDefault();
@@ -565,7 +554,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             if (e.key === 'g') this.collection(e, true);
             if (e.key === 'c' || e.key === 't') this.collection(e);
             if (e.key === 's' || e.key === 'S') this.summary(e);
-            if (e.key === 'b') this.background(e);
             if (e.key === 'p') this.pileup(e);
             this.cleanupInteractions(false);
         }
@@ -575,7 +563,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             e.preventDefault();
             this._lassoFreehand = !this._lassoFreehand;
         }
-    });
+    };
 
     touchesLine(r1: { left: number; top: number; width: number; height: number }) {
         for (const lassoPt of this._lassoPts) {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index bcc2ca129..2a68f9b58 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -13,7 +13,7 @@ import { Docs, DocumentOptions, DocUtils, FInfo } from '../../../documents/Docum
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { SelectionManager } from '../../../util/SelectionManager';
-import { undoBatch } from '../../../util/UndoManager';
+import { undoable, undoBatch } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
@@ -25,6 +25,8 @@ import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
+import { faHeartPulse } from '@fortawesome/free-solid-svg-icons';
+import { faGalacticSenate } from '@fortawesome/free-brands-svg-icons';
 
 export enum ColumnType {
     Number,
@@ -224,7 +226,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @undoBatch
     @action
-    setSort = (field: string | undefined, desc: boolean = false) => {
+    setColumnSort = (field: string | undefined, desc: boolean = false) => {
         this.layoutDoc.sortField = field;
         this.layoutDoc.sortDesc = desc;
     };
@@ -484,24 +486,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         return false;
     };
 
-    @action
-    addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
-        if (!value && !forceEmptyNote) return false;
-        const newDoc = Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true });
-        FormattedTextBox.SelectOnLoad = newDoc[Id];
-        FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
-        return this.addRow(newDoc) || false;
-    };
-
     menuCallback = (x: number, y: number) => {
         ContextMenu.Instance.clearItems();
 
-        DocUtils.addDocumentCreatorMenuItems(doc => this.addRow(doc), this.addRow, x, y, true);
+        DocUtils.addDocumentCreatorMenuItems(this.addRow, this.addRow, x, y, true);
 
-        ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
-            Doc.GetProto(this.props.Document)[name] = '';
-            this.addRow(Docs.Create.TextDocument('', { title: name, _layout_autoHeight: true }));
-        });
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
 
@@ -866,7 +855,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 columnWidths={this.displayColumnWidths}
                                 sortField={this.sortField}
                                 sortDesc={this.sortDesc}
-                                setSort={this.setSort}
+                                setSort={this.setColumnSort}
                                 rowHeight={this.rowHeightFunc}
                                 removeColumn={this.removeColumn}
                                 resizeColumn={this.startResize}
@@ -879,7 +868,14 @@ export class CollectionSchemaView extends CollectionSubView() {
                     {this._columnMenuIndex !== undefined && this.renderColumnMenu}
                     {this._filterColumnIndex !== undefined && this.renderFilterMenu}
                     <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
-                    <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} height={CollectionSchemaView._newNodeInputHeight} />
+                    <EditableView
+                        GetValue={returnEmptyString}
+                        SetValue={undoable(value => (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')}
+                        placeholder={"Type ':' for commands"}
+                        contents={'+ New Node'}
+                        menuCallback={this.menuCallback}
+                        height={CollectionSchemaView._newNodeInputHeight}
+                    />
                 </div>
                 {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
                 {this.previewWidth > 0 && (
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 7da3c042c..46c2f622b 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -5,8 +5,6 @@ import { observer } from 'mobx-react';
 import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { Colors } from '../../global/globalEnums';
 import './CollectionSchemaView.scss';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { DragManager } from '../../../util/DragManager';
 
 export interface SchemaColumnHeaderProps {
     columnKeys: string[];
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 45bfe4f77..978b65053 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -7,7 +7,7 @@ import { Doc } from '../../../../fields/Doc';
 import { BoolCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
-import { undoBatch } from '../../../util/UndoManager';
+import { undoable, undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
@@ -111,18 +111,18 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     }}>
                     <div
                         className="schema-row-button"
-                        onPointerDown={undoBatch(e => {
+                        onPointerDown={undoable(e => {
                             e.stopPropagation();
                             this.props.removeDocument?.(this.rootDoc);
-                        })}>
+                        }, 'Delete Row')}>
                         <FontAwesomeIcon icon="times" />
                     </div>
                     <div
                         className="schema-row-button"
-                        onPointerDown={e => {
+                        onPointerDown={undoable(e => {
                             e.stopPropagation();
                             this.props.addDocTab(this.rootDoc, OpenWhere.addRight);
-                        }}>
+                        }, 'Open Doc on Right')}>
                         <FontAwesomeIcon icon="external-link-alt" />
                     </div>
                 </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 42bf32475..6d5ef9df6 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -122,7 +122,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                         const ret = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
                         this.props.finishEdit?.();
                         return ret;
-                    })}
+                    }, 'edit schema cell')}
                 />
             </div>
         );
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 3f6369898..65d13a6c3 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -49,7 +49,7 @@ export class LinkMenu extends React.Component<Props> {
             <LinkMenuGroup key={group[0]} itemHandler={this.props.itemHandler} docView={this.props.docView} sourceDoc={this.props.docView.props.Document} group={group[1]} groupType={group[0]} clearLinkEditor={this.clear} />
         ));
 
-        return linkItems.length ? linkItems : this.props.style ? [<></>] : [<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>];
+        return linkItems.length ? linkItems : this.props.style ? [] : [<p key="none">No links have been created yet. Drag the linking button onto another document to create a link.</p>];
     };
 
     render() {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 8e83cf121..410e0bbdc 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -447,7 +447,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     timelineScreenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -AudioBox.topControlsHeight);
 
-    setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = time);
+    setPlayheadTime = (time: number) => (this._ele!.currentTime /*= this.layoutDoc._layout_currentTimecode*/ = time);
 
     playing = () => this.mediaState === media_state.Playing;
 
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index d5ca30957..bd1952ecb 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -9,7 +9,7 @@ import { DocUtils } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { Hypothesis } from '../../util/HypothesisUtils';
 import { LinkManager } from '../../util/LinkManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
 import './DocumentLinksButton.scss';
 import { DocumentView } from './DocumentView';
 import { LinkDescriptionPopup } from './LinkDescriptionPopup';
@@ -78,7 +78,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         );
     };
 
-    @undoBatch
     onLinkButtonDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(
             this,
@@ -123,17 +122,14 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             e,
             returnFalse,
             emptyFunction,
-            undoBatch(
-                action(e => {
-                    DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View);
-                })
-            )
+            action(e => DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View))
         );
     };
 
-    public static finishLinkClick = undoBatch(
-        action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) => {
-            if (startLink === endLink) {
+    @undoBatch
+    public static finishLinkClick(screenX: number, screenY: number, startLink: Doc | undefined, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) {
+        runInAction(() => {
+            if (startLink === endLink || !startLink) {
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
                 DocumentLinksButton.AnnotationId = undefined;
@@ -185,8 +181,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                     );
                 }
             }
-        })
-    );
+        });
+    }
 
     @action clearLinks() {
         DocumentLinksButton.StartLink = undefined;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 359b72352..52eee84ac 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -7,6 +7,7 @@ import { AclPrivate, AnimationSym, DataSym, Doc, DocListCast, Field, Opt, StrLis
 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';
@@ -35,8 +36,11 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { DocComponent } from '../DocComponent';
 import { EditableView } from '../EditableView';
 import { GestureOverlay } from '../GestureOverlay';
+import { InkingStroke } from '../InkingStroke';
 import { LightboxView } from '../LightboxView';
+import { OverlayView } from '../OverlayView';
 import { StyleProp } from '../StyleProvider';
+import { UndoStack } from '../UndoStack';
 import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
 import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView';
 import { DocumentLinksButton } from './DocumentLinksButton';
@@ -47,8 +51,6 @@ import { LinkAnchorBox } from './LinkAnchorBox';
 import { PresEffect, PresEffectDirection } from './trails';
 import { PinProps, PresBox } from './trails/PresBox';
 import React = require('react');
-import { InkingStroke } from '../InkingStroke';
-import { RefField } from '../../../fields/RefField';
 const { Howl } = require('howler');
 
 interface Window {
@@ -456,27 +458,26 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         // instead of in the global lightbox
                         const oldFunc = DocumentViewInternal.addDocTabFunc;
                         DocumentViewInternal.addDocTabFunc = this.props.addDocTab;
-                        const res =
-                            this.onClickHandler?.script.run(
-                                {
-                                    this: this.layoutDoc,
-                                    self: this.rootDoc,
-                                    _readOnly_: false,
-                                    scriptContext: this.props.scriptContext,
-                                    documentView: this.props.DocumentView(),
-                                    clientX,
-                                    clientY,
-                                    shiftKey,
-                                    altKey,
-                                    metaKey,
-                                },
-                                console.log
-                            ).result?.select === true
-                                ? this.props.select(false)
-                                : '';
+                        this.onClickHandler?.script.run(
+                            {
+                                this: this.layoutDoc,
+                                self: this.rootDoc,
+                                _readOnly_: false,
+                                scriptContext: this.props.scriptContext,
+                                documentView: this.props.DocumentView(),
+                                clientX,
+                                clientY,
+                                shiftKey,
+                                altKey,
+                                metaKey,
+                            },
+                            console.log
+                        ).result?.select === true
+                            ? this.props.select(false)
+                            : '';
                         DocumentViewInternal.addDocTabFunc = oldFunc;
                     };
-                    clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
+                    clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title));
                 } else {
                     // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
@@ -490,7 +491,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') {
                     this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
                     this._doubleClickTimeout = setTimeout(this._singleClickFunc, 300);
-                } else {
+                } else if (!DocumentView.LongPress) {
                     this._singleClickFunc();
                     this._singleClickFunc = undefined;
                 }
@@ -502,7 +503,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        this._longPressSelector = setTimeout(() => DocumentView.LongPress && this.props.select(false), 1000);
+        this._longPressSelector = setTimeout(() => {
+            if (DocumentView.LongPress) {
+                if (this.rootDoc.dontUndo) {
+                    OverlayView.Instance.addWindow(<UndoStack />, { x: 300, y: 100, width: 200, height: 200, title: 'Undo Stack' });
+                } else {
+                    this.props.select(false);
+                }
+            }
+        }, 1000);
         if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this.props.DocumentView();
 
         this._downX = e.clientX;
@@ -557,7 +566,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (this.onPointerUpHandler?.script) {
             this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
         } else if (e.button === 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
-            this._doubleTap = Date.now() - this._lastTap < Utils.CLICK_TIME;
+            this._doubleTap = this.rootDoc.defaultDoubleClick !== 'ignore' && Date.now() - this._lastTap < Utils.CLICK_TIME;
             if (!this.props.isSelected(true)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
         }
         if (DocumentView.LongPress) e.preventDefault();
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c6172ee01..450176a49 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -123,7 +123,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                                 this._markerTargetDoc = linkTarget;
                                 this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
                             }
-                            this._toolTipText = 'link to ' + this._targetDoc?.title;
                             if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
                         }
                     })
@@ -296,7 +295,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 className="linkDocPreview"
                 ref={this._linkDocRef}
                 onPointerDown={this.followLinkPointerDown}
-                style={{ display: !this._toolTipText ? 'none' : undefined, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+                style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
                 {this.docPreview}
             </div>
         );
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 57aa852ac..b07cf7e00 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -16,7 +16,7 @@ import { CollectionViewType, DocumentType } from '../../../documents/DocumentTyp
 import { LinkManager } from '../../../util/LinkManager';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
@@ -129,29 +129,27 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
      * Number button
      */
     @computed get numberSliderButton() {
-        const numScript = ScriptCast(this.rootDoc.script);
-        const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value');
-
+        const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ self: this.rootDoc, value, _readOnly_: value === undefined });
         // Script for checking the outcome of the toggle
-        const checkResult = Number(numScript?.script.run({ self: this.rootDoc, value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
-
+        const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
         const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
 
         const dropdown = (
             <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}>
                 <input
+                    className="menu-slider"
                     type="range"
                     step="1"
                     min={NumCast(this.rootDoc.numBtnMin, 0)}
                     max={NumCast(this.rootDoc.numBtnMax, 100)}
+                    //readOnly={true}
                     value={checkResult}
-                    className="menu-slider"
-                    onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
+                    onPointerDown={() => (this._batch = UndoManager.StartBatch('num slider changing'))}
                     onPointerUp={() => this._batch?.end()}
-                    onChange={e => {
+                    onChange={undoable(e => {
                         e.stopPropagation();
-                        setValue(Number(e.target.value));
-                    }}
+                        numScript(Number(e.target.value));
+                    }, 'set num value')}
                 />
             </div>
         );
@@ -174,20 +172,13 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
      * Number button
      */
     @computed get numberDropdownButton() {
-        const numScript = ScriptCast(this.rootDoc.script);
-        const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value');
-
-        // Script for checking the outcome of the toggle
-        const checkResult = Number(numScript?.script.run({ self: this.rootDoc, value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
+        const numScript = (value?: number) => ScriptCast(this.rootDoc.script)?.script.run({ self: this.rootDoc, value, _readOnly_: value === undefined });
 
-        const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
+        const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
 
         const items: number[] = [];
-        for (let i = 0; i < 100; i++) {
-            if (i % 2 === 0) {
-                items.push(i);
-            }
-        }
+        for (let i = 0; i < 100; i += 2) items.push(i);
+
         const list = items.map(value => {
             return (
                 <div
@@ -196,15 +187,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     style={{
                         backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined,
                     }}
-                    onClick={() => setValue(value)}>
+                    onClick={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}>
                     {value}
                 </div>
             );
         });
         return (
             <div className="menuButton numBtn list">
-                <div className="button" onClick={action(e => setValue(Number(checkResult) - 1))}>
-                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'minus'} />
+                <div className="button" onClick={undoable(e => numScript(Number(checkResult) - 1), `${this.rootDoc.title} decrement value`)}>
+                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon="minus" />
                 </div>
                 <div
                     className={`button ${'number'}`}
@@ -217,9 +208,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                         this.noTooltip = this.rootDoc.dropDownOpen;
                         Doc.UnBrushAllDocs();
                     })}>
-                    <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={undoBatch(action(e => setValue(Number(e.target.value))))} />
+                    <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} readOnly={true} onChange={undoable(e => numScript(Number(e.target.value)), `${this.rootDoc.title} button set value`)} />
                 </div>
-                <div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}>
+                <div className={`button`} onClick={undoable(e => numScript(Number(checkResult) + 1), `${this.rootDoc.title} increment value`)}>
                     <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
                 </div>
 
@@ -322,12 +313,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             .map(value => (
                 <div
                     className="list-item"
-                    key={`${value}`}
+                    key={value}
                     style={{
                         fontFamily: script.script.originalScript.startsWith('{ return setFont') ? value : undefined,
                         backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined,
                     }}
-                    onClick={undoBatch(() => script.script.run({ self: this.rootDoc, value }))}>
+                    onClick={undoable(() => script.script.run({ self: this.rootDoc, value }), value)}>
                     {value[0].toUpperCase() + value.slice(1)}
                 </div>
             ));
@@ -640,29 +631,26 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
 ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     // prettier-ignore
-    const map: Map<'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+    const map: Map<'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
         ['grid', {
-            undo: false,
             checkResult: (doc:Doc) => doc._freeform_backgroundGrid,
             setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
         }],
         ['snaplines', {
-            undo: false,
             checkResult: (doc:Doc) => doc._freeform_snapLines,
             setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines,
         }],
         ['viewAll', {
-            undo: false,
             checkResult: (doc:Doc) => doc._freeform_fitContentsToBox,
             setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
         }],
         ['clusters', {
-            undo: false,
+            waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
             checkResult: (doc:Doc) => doc._freeform_useClusters,
             setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters,
         }],
         ['arrange', {
-            undo: true,
+            waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
             checkResult: (doc:Doc) => doc._autoArrange,
             setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange,
         }],
@@ -671,7 +659,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snaplines' | 'cluster
     if (checkResult) {
         return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent';
     }
-    const batch = map.get(attr)?.undo ? UndoManager.StartBatch('set feature') : { end: () => {} };
+    const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
     SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv));
     setTimeout(() => batch.end(), 100);
 });
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9c06aa7d8..e85835002 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -301,11 +301,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             this._editorView.updateState(state);
 
             const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
-            const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
-            const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField) : undefined; // the actual text in the text box
-            const curProto = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
-            const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
-            const json = JSON.stringify(state.toJSON());
+            const newText = state.doc.textBetween(0, state.doc.content.size, ' \n');
+            const newJson = JSON.stringify(state.toJSON());
+            const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box
+            const prevLayoutData = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
+            const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const effectiveAcl = GetEffectiveAcl(dataDoc);
 
             const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
@@ -320,29 +320,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 dataDoc.tags = accumTags.length ? new List<string>(Array.from(new Set<string>(accumTags))) : undefined;
 
                 let unchanged = true;
-                if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
+                if (this._applyingChange !== this.fieldKey && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
                     this._applyingChange = this.fieldKey;
-                    const textChange = curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text;
+                    const textChange = newText !== prevData?.Text;
                     textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
-                    if ((!curTemp && !curProto) || curText || json.includes('dash')) {
+                    if ((!prevData && !protoData) || newText || (!newText && !protoData)) {
                         // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
-                        if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
+                        if (removeSelection(newJson) !== removeSelection(prevLayoutData?.Data)) {
                             const numstring = NumCast(dataDoc[this.fieldKey], null);
-                            if (numstring !== undefined) {
-                                dataDoc[this.fieldKey] = Number(curText);
-                            } else {
-                                dataDoc[this.fieldKey] = new RichTextField(json, curText);
-                            }
+                            dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
                             dataDoc[this.fieldKey + '_noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
-                            textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
+                            textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText });
                             unchanged = false;
                         }
                     } else {
                         // if we've deleted all the text in a note driven by a template, then restore the template data
                         dataDoc[this.fieldKey] = undefined;
-                        this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data)));
+                        this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((protoData || prevData).Data)));
                         dataDoc[this.fieldKey + '_noTemplate'] = undefined; // mark the data field as not being split from any template it might have
-                        ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
+                        ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText });
                         unchanged = false;
                     }
                     this._applyingChange = '';
@@ -667,7 +663,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
     };
     sidebarDown = (e: React.PointerEvent) => {
-        const batch = UndoManager.StartBatch('sidebar');
+        const batch = UndoManager.StartBatch('toggle sidebar');
         setupMoveUpEvents(
             this,
             e,
@@ -694,13 +690,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return false;
     };
 
-    @undoBatch
     deleteAnnotation = (anchor: Doc) => {
+        const batch = UndoManager.StartBatch('delete link');
         LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]);
         // const docAnnotations = DocListCast(this.props.dataDoc[this.fieldKey]);
         // this.props.dataDoc[this.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
         // AnchorMenu.Instance.fadeOut(true);
         this.props.select(false);
+        setTimeout(batch.end); // wait for reaction to remove link from document
     };
 
     @undoBatch
@@ -733,17 +730,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 .split(' ')
                 .filter(h => h);
             const anchorDoc = Array.from(hrefs).lastElement().replace(Doc.localServerPath(), '').split('?')[0];
+            const deleteMarkups = undoBatch(() => {
+                const sel = editor.state.selection;
+                editor.dispatch(editor.state.tr.removeMark(sel.from, sel.to, editor.state.schema.marks.linkAnchor));
+            });
             e.persist();
             anchorDoc &&
                 DocServer.GetRefField(anchorDoc).then(
                     action(anchor => {
+                        anchor && SelectionManager.SelectSchemaViewDoc(anchor as Doc);
                         AnchorMenu.Instance.Status = 'annotation';
-                        AnchorMenu.Instance.Delete = () => this.deleteAnnotation(anchor as Doc);
+                        AnchorMenu.Instance.Delete = !anchor && editor.state.selection.empty ? returnFalse : !anchor ? deleteMarkups : () => this.deleteAnnotation(anchor as Doc);
                         AnchorMenu.Instance.Pinned = false;
-                        AnchorMenu.Instance.PinToPres = () => this.pinToPres(anchor as Doc);
-                        AnchorMenu.Instance.MakeTargetToggle = () => this.makeTargetToggle(anchor as Doc);
-                        AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(anchor as Doc);
-                        AnchorMenu.Instance.IsTargetToggler = () => this.isTargetToggler(anchor as Doc);
+                        AnchorMenu.Instance.PinToPres = !anchor ? returnFalse : () => this.pinToPres(anchor as Doc);
+                        AnchorMenu.Instance.MakeTargetToggle = !anchor ? returnFalse : () => this.makeTargetToggle(anchor as Doc);
+                        AnchorMenu.Instance.ShowTargetTrail = !anchor ? returnFalse : () => this.showTargetTrail(anchor as Doc);
+                        AnchorMenu.Instance.IsTargetToggler = !anchor ? returnFalse : () => this.isTargetToggler(anchor as Doc);
                         AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
                     })
                 );
@@ -1002,7 +1004,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     anchorDoc ??
                     Docs.Create.TextConfigDocument({
                         //
-                        title: '#' + this._editorView?.state.doc.textBetween(sel.from, sel.to),
+                        title: 'text(' + this._editorView?.state.doc.textBetween(sel.from, sel.to) + ')',
                         annotationOn: this.dataDoc,
                     });
                 const href = targetHref ?? Doc.localServerPath(anchor);
@@ -1480,11 +1482,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
 
         const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()));
-        if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
+        if (this._editorView && selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             const selLoadChar = FormattedTextBox.SelectOnLoadChar;
             FormattedTextBox.SelectOnLoad = '';
             this.props.select(false);
-            if (selLoadChar && this._editorView) {
+            if (selLoadChar) {
                 const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
                 const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
@@ -1494,10 +1496,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     .insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size)
                     .setStoredMarks(storedMarks);
                 this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
-            } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) {
+            } else if (curText && !FormattedTextBox.DontSelectInitialText) {
                 selectAll(this._editorView.state, this._editorView?.dispatch);
-                this.startUndoTypingBatch();
-            } else if (this._editorView) {
+            } else {
                 this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
             }
         }
@@ -1506,7 +1507,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (this._editorView) {
             const tr = this._editorView.state.tr;
             const { from, to } = tr.selection;
-            // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selectoin after the document has ben fully instantiated.
+            // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selection after the document has ben fully instantiated.
             if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250);
             this._editorView.state.storedMarks = [
                 ...(this._editorView.state.storedMarks ?? []),
@@ -1630,7 +1631,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     onFocused = (e: React.FocusEvent): void => {
         //applyDevTools.applyDevTools(this._editorView);
         this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
-        this.startUndoTypingBatch();
+        e.stopPropagation();
     };
 
     onClick = (e: React.MouseEvent): void => {
@@ -1705,13 +1706,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     }
     startUndoTypingBatch() {
-        !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('undoTyping'));
+        !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('text edits on ' + this.rootDoc.title));
     }
     public endUndoTypingBatch() {
-        const wasUndoing = this._undoTyping;
         this._undoTyping?.end();
         this._undoTyping = undefined;
-        return wasUndoing;
     }
 
     @action
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 3d1d11141..3eadc3047 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1428,6 +1428,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 min={min}
                 max={max}
                 value={value}
+                readOnly={true}
                 style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)` }}
                 className={`toolbar-slider ${active ? '' : 'none'}`}
                 onPointerDown={e => {
@@ -1501,7 +1502,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             <div className="ribbon-doubleButton">
                                 <div className="presBox-subheading">Slide Duration</div>
                                 <div className="ribbon-property">
-                                    <input className="presBox-input" type="number" value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
+                                    <input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
                                 </div>
                                 <div className="ribbon-propertyUpDown">
                                     <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), 1000)}>
@@ -1654,7 +1655,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Zoom (% screen filled)</div>
                             <div className="ribbon-property">
-                                <input className="presBox-input" type="number" value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
+                                <input className="presBox-input" type="number" readOnly={true} value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
                             </div>
                             <div className="ribbon-propertyUpDown">
                                 <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), 0.1)}>
@@ -1669,7 +1670,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                             <div className="presBox-subheading">Transition Time</div>
                             <div className="ribbon-property">
-                                <input className="presBox-input" type="number" value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s
+                                <input className="presBox-input" type="number" readOnly={true} value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s
                             </div>
                             <div className="ribbon-propertyUpDown">
                                 <div className="ribbon-propertyUpDownItem" onClick={() => this.updateTransitionTime(String(transitionSpeed), 1000)}>
@@ -1756,6 +1757,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             className="presBox-input"
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                             type="number"
+                                            readOnly={true}
                                             value={NumCast(activeItem.presStartTime).toFixed(2)}
                                             onKeyDown={e => e.stopPropagation()}
                                             onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
@@ -1782,6 +1784,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                             onKeyDown={e => e.stopPropagation()}
                                             style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
                                             type="number"
+                                            readOnly={true}
                                             value={NumCast(activeItem.presEndTime).toFixed(2)}
                                             onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
                                                 activeItem.presEndTime = Number(e.target.value);
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 4eb6aee25..2279ffe84 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
@@ -13,7 +13,7 @@ import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { SettingsManager } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
+import { undoable, undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
 import { Colors } from '../../global/globalEnums';
@@ -263,16 +263,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    @undoBatch
-    removeItem = action((e: React.MouseEvent) => {
+    removePresentationItem = undoable((e: React.MouseEvent) => {
         e.stopPropagation();
         if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
-            this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
+            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
@@ -476,7 +475,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
         items.push(
             <Tooltip key="trash" title={<div className="dash-tooltip">Remove from presentation</div>}>
-                <div className={'slideButton'} onClick={this.removeItem}>
+                <div className={'slideButton'} onClick={this.removePresentationItem}>
                     <FontAwesomeIcon icon={'trash'} onPointerDown={e => e.stopPropagation()} />
                 </div>
             </Tooltip>
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index d6dddf71a..5480600b0 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -395,22 +395,25 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             ) : (
                 <>
                     <Tooltip key="trash" title={<div className="dash-tooltip">Remove Link Anchor</div>}>
-                        <button className="antimodeMenu-button" onPointerDown={this.Delete}>
+                        <button className="antimodeMenu-button" style={{ display: this.Delete === returnFalse ? 'none' : undefined }} onPointerDown={this.Delete}>
                             <FontAwesomeIcon icon="trash-alt" size="lg" />
                         </button>
                     </Tooltip>
                     <Tooltip key="Pin" title={<div className="dash-tooltip">Pin to Presentation</div>}>
-                        <button className="antimodeMenu-button" onPointerDown={this.PinToPres}>
+                        <button className="antimodeMenu-button" style={{ display: this.PinToPres === returnFalse ? 'none' : undefined }} onPointerDown={this.PinToPres}>
                             <FontAwesomeIcon icon="map-pin" size="lg" />
                         </button>
                     </Tooltip>
                     <Tooltip key="trail" title={<div className="dash-tooltip">Show Linked Trail</div>}>
-                        <button className="antimodeMenu-button" onPointerDown={this.ShowTargetTrail}>
+                        <button className="antimodeMenu-button" style={{ display: this.ShowTargetTrail === returnFalse ? 'none' : undefined }} onPointerDown={this.ShowTargetTrail}>
                             <FontAwesomeIcon icon="taxi" size="lg" />
                         </button>
                     </Tooltip>
                     <Tooltip key="toggle" title={<div className="dash-tooltip">make target visibility toggle on click</div>}>
-                        <button className="antimodeMenu-button" style={{ color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }} onPointerDown={this.MakeTargetToggle}>
+                        <button
+                            className="antimodeMenu-button"
+                            style={{ display: this.IsTargetToggler === returnFalse ? 'none' : undefined, color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }}
+                            onPointerDown={this.MakeTargetToggle}>
                             <FontAwesomeIcon icon="thumbtack" size="lg" />
                         </button>
                     </Tooltip>
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 1c1b41f73..3479cd20f 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -141,6 +141,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
                 const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                 data && newarray.push(...DocListCast(data));
+                const sidebar = d[fieldKey + '_sidebar'];
+                sidebar && newarray.push(...DocListCast(sidebar));
                 func(depth, d);
             });
             docs = newarray;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5312da009..d8447deb6 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -236,7 +236,7 @@ export class Doc extends RefField {
         if (
             doc &&
             Doc.MyFileOrphans instanceof Doc &&
-            Doc.IsDocDataProto(doc) &&
+            Doc.IsDataProto(doc) &&
             !Doc.IsSystem(doc) &&
             ![DocumentType.CONFIG, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
             !doc.isFolder &&
@@ -510,7 +510,7 @@ export namespace Doc {
     export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
         return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
     }
-    export function IsDocDataProto(doc: Doc) {
+    export function IsDataProto(doc: Doc) {
         return GetT(doc, 'isDataDoc', 'boolean', true);
     }
     export function IsBaseProto(doc: Doc) {
@@ -981,7 +981,7 @@ export namespace Doc {
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
         const copy = new Doc(copyProtoId, true);
         updateCachedAcls(copy);
-        const exclude = [...Cast(doc.cloneFieldFilter, listSpec('string'), []), 'dragFactory_count', 'cloneFieldFilter'];
+        const exclude = [...StrListCast(doc.cloneFieldFilter), 'dragFactory_count', 'cloneFieldFilter'];
         Object.keys(doc).forEach(key => {
             if (exclude.includes(key)) return;
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
@@ -1433,7 +1433,7 @@ export namespace Doc {
     export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
         if (!container) return;
         const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'docFilters';
-        const docFilters = Cast(container[filterField], listSpec('string'), []);
+        const docFilters = StrListCast(container[filterField]);
         runInAction(() => {
             for (let i = 0; i < docFilters.length; i++) {
                 const fields = docFilters[i].split(':'); // split key:value:modifier
@@ -1745,6 +1745,16 @@ ScriptingGlobals.add(function undo() {
     SelectionManager.DeselectAll();
     return UndoManager.Undo();
 });
+
+export function ShowUndoStack() {
+    SelectionManager.DeselectAll();
+    var buffer = '';
+    UndoManager.undoStack.forEach((batch, i) => {
+        buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n';
+        ///batch.forEach(undo => (buffer += '   ' + undo.prop + '\n'));
+    });
+    alert(buffer);
+}
 ScriptingGlobals.add(function redo() {
     SelectionManager.DeselectAll();
     return UndoManager.Redo();
diff --git a/src/fields/util.ts b/src/fields/util.ts
index f4fd3200c..a2b445d6c 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -65,6 +65,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     if (value instanceof RefField) {
         value = new ProxyField(value);
     }
+
     if (value instanceof ObjectField) {
         if (value[Parent] && value[Parent] !== receiver && !(value instanceof PrefetchProxy)) {
             throw new Error("Can't put the same object in multiple documents at the same time");
@@ -102,20 +103,24 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
             DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
         }
         !receiver[Initializing] &&
+            !receiver.dontUndo &&
             (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
-            UndoManager.AddEvent({
-                redo: () => (receiver[prop] = value),
-                undo: () => {
-                    const wasUpdate = receiver[UpdatingFromServer];
-                    const wasForce = receiver[ForceServerWrite];
-                    receiver[ForceServerWrite] = true; // needed since writes aren't propagated to server if UpdatingFromServerIsSet
-                    receiver[UpdatingFromServer] = true; // needed if the event caused ACL's to change such that the doc is otherwise no longer editable.
-                    receiver[prop] = curValue;
-                    receiver[ForceServerWrite] = wasForce;
-                    receiver[UpdatingFromServer] = wasUpdate;
+            UndoManager.AddEvent(
+                {
+                    redo: () => (receiver[prop] = value),
+                    undo: () => {
+                        const wasUpdate = receiver[UpdatingFromServer];
+                        const wasForce = receiver[ForceServerWrite];
+                        receiver[ForceServerWrite] = true; // needed since writes aren't propagated to server if UpdatingFromServerIsSet
+                        receiver[UpdatingFromServer] = true; // needed if the event caused ACL's to change such that the doc is otherwise no longer editable.
+                        receiver[prop] = curValue;
+                        receiver[ForceServerWrite] = wasForce;
+                        receiver[UpdatingFromServer] = wasUpdate;
+                    },
+                    prop: prop?.toString(),
                 },
-                prop: prop?.toString(),
-            });
+                value
+            );
         return true;
     }
     return false;
@@ -390,6 +395,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                     diff?.op === '$addToSet'
                         ? {
                               redo: () => {
+                                  console.log('redo $add: ' + prop, diff.items); // bcz: uncomment to log undo
                                   receiver[prop].push(...diff.items.map((item: any) => item.value ?? item));
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
                               },
@@ -406,11 +412,12 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                                   });
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
                               }),
-                              prop: '',
+                              prop: 'add ' + diff.items.length + ' items to list',
                           }
                         : diff?.op === '$remFromSet'
                         ? {
                               redo: action(() => {
+                                  console.log('redo $rem: ' + prop, diff.items); // bcz: uncomment to log undo
                                   diff.items.forEach((item: any) => {
                                       const ind = item instanceof SchemaHeaderField ? receiver[prop].findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading) : receiver[prop].indexOf(item.value ?? item);
                                       ind !== -1 && receiver[prop].splice(ind, 1);
@@ -430,10 +437,11 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                                   });
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
                               },
-                              prop: '',
+                              prop: 'remove ' + diff.items.length + ' items from list',
                           }
                         : {
                               redo: () => {
+                                  console.log('redo list: ' + prop, receiver[prop]); // bcz: uncomment to log undo
                                   receiver[prop] = ObjectField.MakeCopy(newValue as List<any>);
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
                               },
@@ -442,7 +450,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                                   receiver[prop] = ObjectField.MakeCopy(prevValue as List<any>);
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
                               },
-                              prop: '',
+                              prop: 'assign list',
                           }
                 );
         }
-- 
cgit v1.2.3-70-g09d2


From bf16eca7a84adfdf1c5970e7e4793568ee70325d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 13 Jun 2023 22:14:29 -0400
Subject: fixed updating cached docs when opening a backlinks collection. 
 added some FieldInfo types and added 'enumeration' field display in schema
 view.  fixed bug in schema view column sizing.   updated a bunch of standard
 field names to be more consistent.

---
 src/client/documents/Documents.ts                  | 471 +++++++++++----------
 src/client/util/CurrentUserUtils.ts                | 115 ++---
 src/client/util/DocumentManager.ts                 |   2 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/util/LinkManager.ts                     |   2 +-
 src/client/util/TrackMovements.ts                  |   3 +-
 src/client/views/DashboardView.tsx                 |   9 +-
 src/client/views/DocComponent.tsx                  |   2 -
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/DocumentDecorations.tsx           |  16 +-
 src/client/views/FilterPanel.tsx                   |  12 +-
 src/client/views/GestureOverlay.tsx                |   8 +-
 src/client/views/LightboxView.tsx                  |  12 +-
 src/client/views/MainView.tsx                      |  20 +-
 src/client/views/MarqueeAnnotator.tsx              |   2 +-
 src/client/views/OverlayView.tsx                   |   4 +-
 src/client/views/Palette.tsx                       |   4 +-
 src/client/views/PropertiesButtons.tsx             |   4 +-
 .../views/PropertiesDocBacklinksSelector.tsx       |   4 +-
 src/client/views/PropertiesView.tsx                |  44 +-
 src/client/views/SidebarAnnos.tsx                  |  12 +-
 src/client/views/StyleProvider.tsx                 |  23 +-
 src/client/views/TemplateMenu.tsx                  |   4 +-
 src/client/views/collections/CollectionMenu.tsx    |  10 +-
 .../views/collections/CollectionNoteTakingView.tsx | 104 ++---
 .../views/collections/CollectionPileView.tsx       |  21 +-
 .../collections/CollectionStackedTimeline.tsx      |  14 +-
 .../views/collections/CollectionStackingView.tsx   |  12 +-
 src/client/views/collections/CollectionSubView.tsx |  29 +-
 .../views/collections/CollectionTimeView.tsx       |  18 +-
 .../views/collections/CollectionTreeView.tsx       |  12 +-
 src/client/views/collections/TabDocView.tsx        |   8 +-
 src/client/views/collections/TreeView.tsx          |  24 +-
 .../CollectionFreeFormLinkView.tsx                 |  16 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  14 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   2 +-
 .../collectionLinear/CollectionLinearView.tsx      |  18 +-
 .../CollectionMulticolumnView.tsx                  |   4 +-
 .../CollectionMultirowView.tsx                     |   4 +-
 .../collectionSchema/CollectionSchemaView.scss     |   2 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  56 +--
 .../collectionSchema/SchemaColumnHeader.tsx        |   3 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |  10 +-
 .../collectionSchema/SchemaTableCell.tsx           |  55 ++-
 src/client/views/linking/LinkMenuGroup.tsx         |   2 +-
 src/client/views/linking/LinkPopup.tsx             |   4 +-
 .../views/nodes/DataVizBox/components/TableBox.tsx |   2 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   6 +-
 src/client/views/nodes/DocumentView.tsx            |  43 +-
 src/client/views/nodes/KeyValuePair.tsx            |   4 +-
 src/client/views/nodes/LabelBox.tsx                |   4 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |   4 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   4 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   8 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |   8 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |   4 +-
 src/client/views/nodes/QueryBox.scss               |   5 -
 src/client/views/nodes/QueryBox.tsx                |  38 --
 src/client/views/nodes/VideoBox.tsx                |   4 +-
 src/client/views/nodes/WebBox.tsx                  |   8 +-
 src/client/views/nodes/button/FontIconBox.tsx      |   2 +-
 .../views/nodes/formattedText/DashDocView.tsx      |   4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  22 +-
 src/client/views/nodes/trails/PresBox.tsx          |  14 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  10 +-
 src/client/views/pdf/PDFViewer.tsx                 |  10 +-
 src/fields/Doc.ts                                  |  44 +-
 src/fields/documentSchemas.ts                      |  10 +-
 src/fields/util.ts                                 |   8 +-
 src/mobile/AudioUpload.tsx                         |  10 +-
 src/mobile/MobileInterface.tsx                     |   4 +-
 72 files changed, 740 insertions(+), 761 deletions(-)
 delete mode 100644 src/client/views/nodes/QueryBox.scss
 delete mode 100644 src/client/views/nodes/QueryBox.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c1abd6e71..a91d5806c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -110,18 +110,28 @@ class DocInfo extends FInfo {
     }
 }
 class DimInfo extends FInfo {
-    fieldType? = 'DimUnit';
+    fieldType? = 'enumeration';
     values? = [DimUnit.Pixel, DimUnit.Ratio];
-    readOnly = true;
+    readOnly = false;
 }
 class PEInfo extends FInfo {
-    fieldType? = 'pointerEvents';
+    fieldType? = 'enumeration';
     values? = ['all', 'none'];
-    readOnly = true;
+    readOnly = false;
 }
 class DAInfo extends FInfo {
-    fieldType? = 'dropActionType';
+    fieldType? = 'enumeration';
     values? = ['embed', 'copy', 'move', 'same', 'proto', 'none'];
+    readOnly = false;
+}
+class CTypeInfo extends FInfo {
+    fieldType? = 'enumeration';
+    values? = Array.from(Object.keys(CollectionViewType));
+    readOnly = false;
+}
+class DTypeInfo extends FInfo {
+    fieldType? = 'enumeration';
+    values? = Array.from(Object.keys(DocumentType));
     readOnly = true;
 }
 class DateInfo extends FInfo {
@@ -134,272 +144,267 @@ type STRt = StrInfo | string;
 type DOCt = DocInfo | Doc;
 type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
 type PEVt = PEInfo | 'none' | 'all';
+type COLLt = CTypeInfo | CollectionViewType;
 type DROPt = DAInfo | dropActionType;
 type DATEt = DateInfo | number;
+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');
     y?: NUMt = new NumInfo('y coordinage of document in a freeform view');
     z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, [1, 0]);
-    isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
-    type?: STRt = new StrInfo('type of document', true, Array.from(Object.keys(DocumentType)));
-    title?: string;
-    author_date?: DATEt = new DateInfo('date the document was created', true);
-    _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
-    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true);
-    childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
-    targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
-    userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
-    color?: STRt = new StrInfo('foreground color data doc');
-    backgroundColor?: STRt = new StrInfo('background color for data doc');
-    _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
-    _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
-    _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
-    _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
-    _freeform_panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
-    _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
+    _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
+    _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
+    lat?: NUMt = new NumInfo('latitude coordinate for map views');
+    lng?: NUMt = new NumInfo('longitude coordinate for map views');
+    _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)');
+    _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden');
     _width?: NUMt = new NumInfo('displayed width of a document');
     _height?: NUMt = new NumInfo('displayed height of document');
     data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)');
     data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)');
+    linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)');
     _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)');
     _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)');
     _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
     _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
-    _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
-    _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
+
+    'acl-Public'?: string; // public permissions
+    '_acl-Public'?: string; // public permissions
+    type?: DTYPEt = new DTypeInfo('type of document', true);
+    _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection
+    title?: STRt = new StrInfo('title of document');
+    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);
+    annotationOn?: DOCt = new DocInfo('document annotated by this document');
+    color?: STRt = new StrInfo('foreground color data doc');
+    hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection');
+    backgroundColor?: STRt = new StrInfo('background color for data doc');
+    opacity?: NUMt = new NumInfo('document opacity');
+    viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters');
+    dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc');
+    dontUndo?: BOOLt = new BoolInfo('whether button clicks should be undoable (true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)');
+    _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
+    _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
+    _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
+    _lockedPosition?: BOOLt = new BoolInfo("lock the x,y coordinates of the document so that it can't be dragged");
+    _lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed');
+
+    layout?: string | Doc; // default layout string or template document
+    layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document');
+    layout_explainer?: STRt = new StrInfo('explanation displayed at top of a collection to describe its purpose');
+    layout_headerButton?: DOCt = new DocInfo('the (button) Doc to display at the top of a collection.');
+    layout_disableBrushing?: BOOLt = new BoolInfo('whether to suppress border highlighting');
+    layout_unrendered?: BOOLt = new BoolInfo('denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)');
+    layout_hideOpenButton?: BOOLt = new BoolInfo('whether to hide the open full screen button when selected');
+    layout_hideDocumentButtonBar?: BOOLt = new BoolInfo('whether to hide the document decorations lower button bar when selected');
+    layout_hideLinkAnchors?: BOOLt = new BoolInfo('suppresses link anchor dots from being displayed');
+    layout_hideAllLinks?: BOOLt = new BoolInfo('whether all individual blue anchor dots should be hidden');
+    layout_hideResizeHandles?: BOOLt = new BoolInfo('whether to hide the resize handles when selected');
+    layout_hideLinkButton?: BOOLt = new BoolInfo('whether the blue link counter button should be hidden');
+    layout_hideDecorationTitle?: BOOLt = new BoolInfo('whether to suppress the document decortations title when selected');
+    layout_borderRounding?: string;
+    layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
+    layout_maxAutoHeight?: NUMt = new NumInfo('maximum height for newly created (eg, from pasting) text documents');
+    _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
+    _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document');
+    _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video)  value is in seconds');
+    _layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown');
     _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
-    _layoutFitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
-    _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
-    _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
-    _lockedTransform?: boolean; // lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed
-    _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target
+    _layout_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
+    _layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition');
+    _layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button');
     _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
-    _layout_altContentUI?: boolean; // whether to show alternate content button
-    _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target
+    _layout_showSidebar?: BOOLt = new BoolInfo('whether an annotationsidebar should be displayed for text docuemnts');
     _layout_showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
-    _layoutScrollTop?: number; // scroll location for pdfs
-    _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged
-    _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
-    _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
-    _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active)
-    enableDragWhenActive?: boolean; // allow dragging even if document contentts are active (e.g., tree, groups)
-    _stayInCollection?: boolean; // whether the document should remain in its collection when someone tries to drag and drop it elsewhere
-    _raiseWhenDragged?: boolean; // whether a document is brought to front when dragged.
-    _hideContextMenu?: boolean; // whether the context menu can be shown
-    _type_collection?: string; // sub type of a collection
-    type_collection?: string; // sub type of a collection
-    _gridGap?: number; // gap between items in masonry view
-    freeform?: string; // placeholder just so that the key value pane has a divider to render for freeform view data
-    _freeform_scale?: number; // how much a freeform view has been scaled (zoomed)
-    _overflow?: string; // set overflow behavior
-    _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts
-    _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts
-    _xPadding?: number;
-    _yPadding?: number;
-    _carousel_index?: number; // which item index the carousel viewer is showing
-    _layout_showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts
+
+    _chromeHidden?: BOOLt = new BoolInfo('whether the editing chrome for a document is hidden');
+    _gridGap?: NUMt = new NumInfo('gap between items in masonry view');
+    _xMargin?: NUMt = new NumInfo('gap between left edge of document and start of masonry/stacking layouts');
+    _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts');
+    _xPadding?: NUMt = new NumInfo('x padding');
+    _yPadding?: NUMt = new NumInfo('y padding');
     _singleLine?: boolean; // whether label box is restricted to one line of text
     _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents
-    _minFontSize?: number; // minimum font size for labelBoxes
-    _maxFontSize?: number; // maximum font size for labelBoxes
-    _columnWidth?: number;
-    _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
-    _text_fontSize?: string;
-    _text_fontFamily?: string;
-    _text_fontWeight?: string;
-    _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
-    _layout_curPage?: number; // current page of a PDF or other? paginated document
-    _layout_currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video)  value is in seconds
-    _currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide)
-    _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
-    _timecodeToHide?: number; // the time that a document should be hidden
-    _timelineLabel?: boolean; // whether the document exists on a timeline
+    _columnWidth?: NUMt = new NumInfo('width of table column');
+    _columnsHideIfEmpty?: BOOLt = new BoolInfo('whether stacking view column headings should be hidden');
     _caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', true);
     _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', true);
     icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', true);
     icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', true);
-    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
-    openFactoryLocation?: string; // an OpenWhere value to place the factory created document
-    openFactoryAsDelegate?: boolean; //
-    lat?: number;
-    lng?: number;
-    infoWindowOpen?: boolean;
-    author?: string;
-    _layout_fieldKey?: string;
-    fieldValues?: List<any>; // possible field values used by fieldInfos
-    fieldType?: string; // type of afield used by fieldInfos
-    layout_unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)
-    'acl-Public'?: string; // public permissions
-    '_acl-Public'?: string; // public permissions
-    version?: string; // version identifier for a document
-    label?: string;
-    hidden?: boolean;
-    _hidden?: boolean;
-    pointerEvents?: string; // pointer events that the documentview should have
-    mediaState?: string; // status of audio/video media document: "pendingRecording", "recording", "paused", "playing"
-    recording?: boolean; // whether WebCam is recording or not
-    autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline.
-    dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it.
-    linkSource?: Doc; // the source document for a collection of backlinks
+    _text_fontSize?: string;
+    _text_fontFamily?: string;
+    _text_fontWeight?: string;
+    _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
+
+    infoWindowOpen?: BOOLt = new BoolInfo('whether info window corresponding to pin is open (on MapDocuments)');
+    _carousel_index?: NUMt = new NumInfo('which item index the carousel viewer is showing');
+    _label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes');
+    _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes');
+    stroke_width?: NUMt = new NumInfo('width of an ink stroke');
+    icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)');
+    mediaState?: STRt = new StrInfo('status of audio/video media document: "pendingRecording", "recording", "paused", "playing"');
+    recording?: BOOLt = new BoolInfo('whether WebCam is recording or not');
+    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.');
     updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything)
     toolTip?: string; // tooltip to display on hover
     toolType?: string; // type of pen tool
-    expertMode?: boolean; // something available only in expert (not novice) mode
+    expertMode?: BOOLt = new BoolInfo('something available only in expert (not novice) mode');
+
+    contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     contextMenuFilters?: List<ScriptField>;
     contextMenuScripts?: List<ScriptField>;
     contextMenuLabels?: List<string>;
     contextMenuIcons?: List<string>;
-    defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
-    waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
-    dontUndo?: boolean; // whether button clicks should be undoable ( true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)
-    layout?: string | Doc; // default layout string for a document
-    contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
-    childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
+    childFilters_boolean?: string;
+    childFilters?: List<string>;
+    childLimitHeight?: NUMt = new NumInfo('whether to limit the height of collection children.  0 - means  height can be no bigger than width');
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
-    childDocumentsActive?: boolean; // whether child documents are active when parent is document active
-    childDontRegisterViews?: boolean;
-    childHideLinkButton?: boolean; // hide link buttons on all children
+    childDocumentsActive?: BOOLt = new BoolInfo('whether child documents are active when parent is document active');
+    childDontRegisterViews?: BOOLt = new BoolInfo('whether child document views should be registered so that they can be found when following links, etc');
+    childHideLinkButton?: BOOLt = new BoolInfo('hide link buttons on all children');
     childContextMenuFilters?: List<ScriptField>;
     childContextMenuScripts?: List<ScriptField>;
     childContextMenuLabels?: List<string>;
     childContextMenuIcons?: List<string>;
-    followLinkZoom?: boolean; // whether to zoom to the target of a link
-    layout_hideLinkButton?: boolean; // whether the blue link counter button should be hidden
-    disableDocBrushing?: boolean; // whether to suppress border highlighting
-    layout_hideDecorationTitle?: boolean;
-    hideOpenButton?: boolean;
-    layout_hideResizeHandles?: boolean;
-    hideDocumentButtonBar?: boolean;
-    hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
-    isTemplateForField?: string; // the field key for which the containing document is a rendering template
-    isTemplateDoc?: boolean;
     targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc)
-    templates?: List<string>;
-    hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images)
-    caption?: RichTextField;
-    opacity?: number;
-    defaultBackgroundColor?: string;
-    _layout_autoMoveAnchors?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint
-    layout_hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed
-    isFolder?: boolean;
-    lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide)
-    activeFrame?: number; // the active frame of a document in a frame base collection
-    appearFrame?: number; // the frame in which the document appears
-    viewTransitionTime?: number; // transition duration for view parameters
-    presPanX?: number; // panX saved as a view spec
-    presPanY?: number; // panY saved as a view spec
-    presViewScale?: number; // viewScale saved as a view Spec
-    presTransition?: number; //the time taken for the transition TO a document
-    presDuration?: number; //the duration of the slide in presentation view
-    presZoomText?: boolean; // whether text anchors should shown in a larger box when following links to make them stand out
-    borderRounding?: string;
-    boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
+
+    lastFrame?: NUMt = new NumInfo('the last frame of a frame-based collection (e.g., progressive slide)');
+    activeFrame?: NUMt = new NumInfo('the active frame of a document in a frame base collection');
+    appearFrame?: NUMt = new NumInfo('the frame in which the document appears');
+    _currentFrame?: NUMt = new NumInfo('the current frame of a frame-based collection (e.g., progressive slide)');
+
+    isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
+    isBaseProto?: BOOLt = new BoolInfo('is doc a base level prototype for data documents as opposed to data documents which are prototypes for layout documents.  base protos are not cloned during a deep');
+    isTemplateForField?: string; // the field key for which the containing document is a rendering template
+    isTemplateDoc?: BOOLt = new BoolInfo('is the document a template for creating other documents');
+    isGroup?: BOOLt = new BoolInfo('should collection use a grouping UI behavior');
+    isFolder?: BOOLt = new BoolInfo('is document a tree view folder');
+    _isTimelineLabel?: BOOLt = new BoolInfo('is document a timeline label');
+    _isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target');
+
+    presPanX?: NUMt = new NumInfo('panX saved as a view spec');
+    presPanY?: NUMt = new NumInfo('panY saved as a view spec');
+    presViewScale?: NUMt = new NumInfo('viewScale saved as a view Spec');
+    presTransition?: NUMt = new NumInfo('the time taken for the transition TO a document');
+    presDuration?: NUMt = new NumInfo('the duration of the slide in presentation view');
+    presZoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out');
+
     data?: any;
-    isBaseProto?: boolean; // this Doc is base level prototype for data documents as opposed to data documents which are prototypes for layout documents.  base protos are not cloned during a deep
-    dontRegisterView?: boolean;
-    lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document.  see PresBox.
+    data_useCors?: BOOLt = new BoolInfo('whether CORS protocol should be used for web page');
     columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
     schemaHeaders?: List<SchemaHeaderField>; // headers for schema view
-    dockingConfig?: string;
-    annotationOn?: Doc;
-    followLinkToggle?: boolean;
-    isGroup?: boolean; // whether a collection should use a grouping UI behavior
-    _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
+    dockingConfig?: STRt = new StrInfo('configuration of golden layout windows (applies only if doc is rendered as a CollectionDockingView)');
+    icon?: string; // icon used by fonticonbox to render button
     noteType?: string;
-    // BACKGROUND GRID
+
+    // freeform properties
     _freeform_backgroundGrid?: boolean;
+    _freeform_scale?: NUMt = new NumInfo('how much a freeform view has been scaled (zoomed)');
+    _freeform_panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
+    _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
+    _freeform_noAutoPan?: BOOLt = new BoolInfo('disables autopanning when this item is dragged');
+    _freeform_noZoom?: BOOLt = new BoolInfo('disables zooming');
 
     //BUTTONS
     buttonText?: string;
-    iconShape?: string; // shapes of the fonticon border
     btnType?: string;
     btnList?: List<string>;
     docColorBtn?: string;
     userColorBtn?: string;
-    canClick?: string;
     script?: ScriptField;
-    numBtnMax?: number;
-    numBtnMin?: number;
+    numBtnMax?: NUMt = new NumInfo('maximum value of a number button');
+    numBtnMin?: NUMt = new NumInfo('minimum value of a number button');
     switchToggle?: boolean;
     badgeValue?: ScriptField;
 
     //LINEAR VIEW
-    linearViewIsExpanded?: boolean; // is linear view expanded
-    linearViewExpandable?: boolean; // can linear view be expanded
-    linearViewToggleButton?: string; // button to open close linear view group
-    linearViewSubMenu?: boolean;
-    linearBtnWidth?: number;
-    flexGap?: number; // Linear view flex gap
+    linearView_IsExpanded?: BOOLt = new BoolInfo('is linear view expanded');
+    linearView_Expandable?: BOOLt = new BoolInfo('can linear view be expanded');
+    linearView_SubMenu?: BOOLt = new BoolInfo('is doc a sub menu of more linear views');
+    flexGap?: NUMt = new NumInfo('Linear view flex gap');
     flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse';
 
-    layout_linkView?: Doc; // view template for a link document
-    layout_keyValue?: string; // view tempalte for key value docs
     link_description?: string; // added for links
     link_relationship?: string; // type of relatinoship a link represents
-    layout_linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors
-    layout_linkDisplayArrow?: boolean; // whether to display link's directional arrowhead
+    link_displayLine?: BOOLt = new BoolInfo('whether a link line should be dipslayed between the two link anchors');
+    link_displayArrow?: BOOLt = new BoolInfo("whether to display link's directional arrowhead");
     link_anchor_1?: Doc;
     link_anchor_2?: Doc;
-    link_anchor_1_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
-    link_anchor_2_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
-    ignoreClick?: boolean;
+    link_autoMoveAnchors?: BOOLt = new BoolInfo('whether link endpoint should move around the edges of a document to make shortest path to other link endpoint');
+    link_anchor_1_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)');
+    link_anchor_2_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)');
+    link_relationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
+    link_relationshipSizes?: List<number>; //stores number of links contained in each relationship
+    link_colorList?: List<string>; // colors of links corresponding to specific link relationships
+    followLinkZoom?: BOOLt = new BoolInfo('whether to zoom to the target of a link');
+    followLinkToggle?: BOOLt = new BoolInfo('whether target of link should be toggled on and off when following a link to it');
+    followLinkLocation?: STRt = new StrInfo('where to open link target when following link');
+    followLinkAnimEffect?: STRt = new StrInfo('animation effect triggered on target of link');
+    followLinkAnimDirection?: STRt = new StrInfo('direction modifier for animation effect');
+
+    ignoreClick?: BOOLt = new BoolInfo('whether clicks on document should be ignored');
     onClick?: ScriptField;
     onDoubleClick?: ScriptField;
     onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked
     onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked
+    defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
+    waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     onPointerDown?: ScriptField;
     onPointerUp?: ScriptField;
+    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
+    openFactoryLocation?: string; // an OpenWhere value to place the factory created document
+    openFactoryAsDelegate?: BOOLt = new BoolInfo('create a delegate of the factory');
+    _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)');
+    enableDragWhenActive?: BOOLt = new BoolInfo('allow dragging even if document contentts are active (e.g., tree, groups)');
+    _stayInCollection?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere');
+    _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.');
+    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true);
+    childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
+    targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
+    _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
+    _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
+    cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
     dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
     dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
-    cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
-    filterBoolean?: string;
-    data_useCors?: boolean;
-    icon?: string; // icon used by fonticonbox to render button
-    target?: Doc; // available for use in scripts as the primary target document
-    sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
-    targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
-    searchFileTypes?: List<string>; // file types allowed in a search query
-    stroke_width?: number;
-    freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection
-    treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
-    treeViewHideUnrendered?: boolean; // tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeViewFieldKey tag (presBox)
-    treeViewHideHeaderIfTemplate?: boolean; // whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)
-    treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
-    treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
+    target?: Doc; // available for use in scripts.  used to provide a document parameter to the script (Note, this is a convenience entry since any field could be used for parameterizing a script)
+
+    treeViewHideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view');
+    treeViewHideUnrendered?: BOOLt = new BoolInfo("tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeViewFieldKey tag (presBox)");
+    treeViewHideHeaderIfTemplate?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)');
+    treeViewHideHeader?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view');
+    treeViewHideHeaderFields?: BOOLt = new BoolInfo('whether to hide the drop down options for tree view items.');
     treeViewChildDoubleClick?: ScriptField; //
-    // Action Button
-    buttonMenu?: boolean; // whether a action button should be displayed
-    buttonMenuDoc?: Doc;
-    explainer?: string;
-
-    treeViewOpenIsTransient?: boolean; // ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view
-    _treeViewOpen?: boolean; // whether this document is expanded in a tree view  (note: need _ and regular versions since this can be specified for both proto and layout docs)
-    treeViewOpen?: boolean; // whether this document is expanded in a tree view
+    treeViewOpenIsTransient?: BOOLt = new BoolInfo("ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view");
+    treeViewOpen?: BOOLt = new BoolInfo('whether this document is expanded in a tree view');
     treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
-    treeViewExpandedViewLock?: boolean; // whether the expanded view can be changed
+    treeViewExpandedViewLock?: BOOLt = new BoolInfo('whether the expanded view can be changed');
     treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
-    treeViewTruncateTitleWidth?: number;
-    treeViewHasOverlay?: boolean; // whether the treeview has an overlay for freeform annotations
+    treeViewTruncateTitleWidth?: NUMt = new NumInfo('maximum width of a treew view title before truncation');
+    treeViewHasOverlay?: BOOLt = new BoolInfo('whether the treeview has an overlay for freeform annotations');
     treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
-    sidebarColor?: string; // background color of text sidebar
-    sidebarViewType?: string; // collection type of text sidebar
-    docMaxAutoHeight?: number; // maximum height for newly created (eg, from pasting) text documents
+    treeViewFreezeChildren?: STRt = new StrInfo('set (add, remove, add|remove) to disable adding, removing or both from collection');
+
+    sidebar_color?: string; // background color of text sidebar
+    sidebar_collectionType?: string; // collection type of text sidebar
+
     text?: string;
-    textTransform?: string; // is linear view expanded
-    letterSpacing?: string; // is linear view expanded
+    textTransform?: string;
+    letterSpacing?: string;
     iconTemplate?: string; // name of icon template style
-    selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui
+    selectedIndex?: NUMt = new NumInfo("which item in a linear view has been selected using the 'thumb doc' ui");
+
+    fieldValues?: List<any>; // possible values a field can have (used by FieldInfo's only)
+    fieldType?: string; // display type of a field, e.g. string, number, enumeration (used by FieldInfo's only)
+
     clipboard?: Doc;
-    searchQuery?: string; // for quersyBox
-    useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox
-    border?: string; //for searchbox
     hoverBackgroundColor?: string; // background color of a label when hovered
-    link_relationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
-    link_relationshipSizes?: List<number>; //stores number of links contained in each relationship
-    linkColorList?: List<string>; // colors of links corresponding to specific link relationships
+    userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
 }
 export namespace Docs {
     export let newAccount: boolean = false;
@@ -986,7 +991,7 @@ export namespace Docs {
             I.layout = InkingStroke.LayoutString('stroke');
             I.layout_fitWidth = true;
             I.layout_hideDecorationTitle = true; // don't show title when selected
-            // I.hideOpenButton = true; // don't show open full screen button when selected
+            // I.layout_hideOpenButton = true; // don't show open full screen button when selected
             I.color = color;
             I.fillColor = fillColor;
             I.stroke = new InkField(points);
@@ -1088,7 +1093,12 @@ export namespace Docs {
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: 'visible', enableDragWhenActive: true, _forceActive: true, _noAutoscroll: true, ...options, _type_collection: CollectionViewType.Pile }, id);
+            return InstanceFromProto(
+                Prototypes.get(DocumentType.COL),
+                new List(documents),
+                { enableDragWhenActive: true, _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
+                id
+            );
         }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1177,12 +1187,7 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(
-                Prototypes.get(DocumentType.COL),
-                new List(documents),
-                { freezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, _type_collection: CollectionViewType.Docking, dockingConfig: config },
-                id
-            );
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, _type_collection: CollectionViewType.Docking, dockingConfig: config }, id);
         }
 
         export function DirectoryImportDocument(options: DocumentOptions = {}) {
@@ -1227,18 +1232,18 @@ export namespace Docs {
 export namespace DocUtils {
     /**
      * @param docs
-     * @param docFilters
-     * @param docRangeFilters
+     * @param childFilters
+     * @param childFiltersByRanges
      * @param parentCollection
-     * Given a list of docs and docFilters, @returns the list of Docs that match those filters
+     * Given a list of docs and childFilters, @returns the list of Docs that match those filters
      */
-    export function FilterDocs(childDocs: Doc[], docFilters: string[], docRangeFilters: string[], parentCollection?: Doc) {
-        if (!docFilters?.length && !docRangeFilters?.length) {
+    export function FilterDocs(childDocs: Doc[], childFilters: string[], childFiltersByRanges: string[], parentCollection?: Doc) {
+        if (!childFilters?.length && !childFiltersByRanges?.length) {
             return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one
         }
 
         const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
-        docFilters.forEach(filter => {
+        childFilters.forEach(filter => {
             const fields = filter.split(':');
             const key = fields[0];
             const value = fields[1];
@@ -1249,7 +1254,7 @@ export namespace DocUtils {
             filterFacets[key][value] = modifiers;
         });
 
-        const filteredDocs = docFilters.length
+        const filteredDocs = childFilters.length
             ? childDocs.filter(d => {
                   if (d.z) return true;
                   // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter
@@ -1293,7 +1298,7 @@ export namespace DocUtils {
                                 return Field.toString(d[facetKey] as Field).includes(value);
                             });
                       // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
-                      if (parentCollection?.filterBoolean === 'OR') {
+                      if (parentCollection?.childFilters_boolean === 'OR') {
                           if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
                       }
                       // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
@@ -1301,14 +1306,14 @@ export namespace DocUtils {
                           if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
                       }
                   }
-                  return (parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR' ? false : true;
+                  return (parentCollection?.currentFilter as Doc)?.childFilters_boolean === 'OR' ? false : true;
               })
             : childDocs;
         const rangeFilteredDocs = filteredDocs.filter(d => {
-            for (let i = 0; i < docRangeFilters.length; i += 3) {
-                const key = docRangeFilters[i];
-                const min = Number(docRangeFilters[i + 1]);
-                const max = Number(docRangeFilters[i + 2]);
+            for (let i = 0; i < childFiltersByRanges.length; i += 3) {
+                const key = childFiltersByRanges[i];
+                const min = Number(childFiltersByRanges[i + 1]);
+                const max = Number(childFiltersByRanges[i + 2]);
                 const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null);
                 if (val === undefined) {
                     //console.log("Should 'undefined' pass range filter or not?")
@@ -1325,11 +1330,11 @@ export namespace DocUtils {
         broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
         return DocUtils.ActiveRecordings.map(audio => {
             const sourceDoc = getSourceDoc();
-            return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { layout_linkDisplay: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' });
+            return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' });
         });
     }
 
-    export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; layout_linkDisplay?: boolean }, id?: string, showPopup?: number[]) {
+    export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) {
         if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
         const sv = DocumentManager.Instance.getDocumentView(source);
         if (target.doc === Doc.UserDoc()) return undefined;
@@ -1370,15 +1375,15 @@ export namespace DocUtils {
                 source,
                 target,
                 {
-                    title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
-                    link_anchor_1_useLinkSmallAnchor: source.useLinkSmallAnchor ? true : undefined,
-                    link_anchor_2_useLinkSmallAnchor: target.useLinkSmallAnchor ? true : undefined,
                     'acl-Public': SharingPermissions.Augment,
                     '_acl-Public': SharingPermissions.Augment,
-                    layout_linkDisplay: linkSettings.layout_linkDisplay,
+                    title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
+                    link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined,
+                    link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined,
+                    link_displayLine: linkSettings.link_displayLine,
                     link_relationship: linkSettings.link_relationship,
                     link_description: linkSettings.link_description,
-                    _layout_autoMoveAnchors: true,
+                    link_autoMoveAnchors: true,
                     _layout_showCaption: 'link_description',
                     _layout_showTitle: 'link_relationship',
                 },
@@ -1706,14 +1711,14 @@ export namespace DocUtils {
         if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
             const pushpin = Docs.Create.FontIconDocument({
                 title: 'pushpin',
-                label: '',
+                icon_label: '',
                 annotationOn: Cast(doc.annotationOn, Doc, null),
                 followLinkToggle: true,
                 icon: 'map-pin',
                 x: Cast(doc.x, 'number', null),
                 y: Cast(doc.y, 'number', null),
                 backgroundColor: '#ACCEF7',
-                hideAllLinks: true,
+                layout_hideAllLinks: true,
                 _width: 15,
                 _height: 15,
                 _xPadding: 0,
@@ -1804,7 +1809,7 @@ export namespace DocUtils {
             _xMargin: noMargins ? 0 : undefined,
             _yMargin: noMargins ? 0 : undefined,
             annotationOn,
-            docMaxAutoHeight: maxHeight,
+            layout_maxAutoHeight: maxHeight,
             backgroundColor,
             _width: width || 200,
             _height: 35,
@@ -1812,7 +1817,7 @@ export namespace DocUtils {
             y: y,
             _layout_fitWidth: true,
             _layout_autoHeight: true,
-            _layout_altContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
+            _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
             title,
         });
         const template = Doc.UserDoc().defaultTextLayout;
@@ -1842,18 +1847,18 @@ export namespace DocUtils {
     }
 
     /**
-     * uploadFilesToDocs will take in an array of Files, and creates documents for the 
-     * new files. 
-     * 
+     * uploadFilesToDocs will take in an array of Files, and creates documents for the
+     * new files.
+     *
      * @param files an array of files that will be uploaded
      * @param options options to use while uploading
-     * @returns 
+     * @returns
      */
     export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
 
-        // These files do not have overwriteDocs, so we do not set the guid and let the client generate one. 
-        const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({file}));
+        // These files do not have overwriteDocs, so we do not set the guid and let the client generate one.
+        const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file }));
 
         const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs);
         for (const {
@@ -1867,8 +1872,8 @@ export namespace DocUtils {
 
     export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) {
         const generatedDocuments: Doc[] = [];
-        // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. 
-        Networking.UploadFilesToServer([{file, guid: overwriteDoc[Id]}]).then(upfiles => {
+        // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid.
+        Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => {
             const {
                 source: { name, type },
                 result,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 6f6ac2d4a..59041862f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -23,7 +23,7 @@ import { MainView } from "../views/MainView";
 import { ButtonType } from "../views/nodes/button/FontIconBox";
 import { OpenWhere } from "../views/nodes/DocumentView";
 import { OverlayView } from "../views/OverlayView";
-import { DragManager } from "./DragManager";
+import { DragManager, dropActionType } from "./DragManager";
 import { MakeTemplate } from "./DropConverter";
 import { FollowLinkScript } from "./LinkFollower";
 import { LinkManager } from "./LinkManager";
@@ -97,7 +97,7 @@ export class CurrentUserUtils {
 
         const reqdOpts:DocumentOptions = {
             title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true,
-            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, isSystem: true,
+            _stayInCollection: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true,
             _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
@@ -136,7 +136,7 @@ export class CurrentUserUtils {
         const reqdClickList = reqdTempOpts.map(opts => {
             const allOpts = {...reqdClickOpts, ...opts.opts};
             const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined;
-            return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title);
+            return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title?.toString());
         });
 
         const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true};
@@ -192,7 +192,7 @@ export class CurrentUserUtils {
                 {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
         };
         const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
-            textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+            textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 24, layout_borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
         const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _layout_showTitle: "title", ...opts });
         const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
@@ -260,10 +260,11 @@ export class CurrentUserUtils {
         const emptyThings:{key:string,  // the field name where the empty thing will be stored
                           opts:DocumentOptions,   // the document options that are required for the empty thing
                           funcs?:{[key:string]: any},  // computed fields that are rquired for the empth thing
+                          scripts?:{[key:string]: any},
                           creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
               }[] = [
             {key: "Note",       creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _layout_autoHeight: true }},
-            {key: "Flashcard",  creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _layout_autoHeight: true, _layout_altContentUI: true}},
+            {key: "Flashcard",  creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _layout_autoHeight: true, _layout_enableAltContentUI: true}},
             {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, }},
             {key: "Noteboard",  creator: opts => Docs.Create.NoteTakingDocument([], opts),  opts: { _width: 250, _height: 200, _layout_fitWidth: true}},
             {key: "Simulation", creator: opts => Docs.Create.SimulationDocument(opts),      opts: { _width: 300, _height: 300, }},
@@ -274,11 +275,11 @@ export class CurrentUserUtils {
             {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _layout_fitWidth: true, _layout_showSidebar: true,  }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
             {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
-            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: FollowLinkScript()}},
+            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10}, 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, treeViewHideUnrendered: true}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, targetDropAction: "embed" as any, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, targetDropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
                                                                                                 treeViewHasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
@@ -287,7 +288,7 @@ export class CurrentUserUtils {
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
 
-        emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs));
+        emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs));
         
         return [
             { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc,       clickFactory: DocCast(doc.emptyNote)},
@@ -321,7 +322,7 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
             const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, 
-                _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, 
+                _width: 35, _height: 35, _layout_hideContextMenu: true, _stayInCollection: true, 
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
@@ -329,7 +330,7 @@ export class CurrentUserUtils {
         });
 
         const reqdOpts:DocumentOptions = {
-            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, isSystem: true,
+            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
             _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
              childDropAction: 'embed' 
         };
@@ -367,14 +368,14 @@ export class CurrentUserUtils {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
                 title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true,
-                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true,
+                _width: 60, _height: 60,  _stayInCollection: true, _layout_hideContextMenu: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
             };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
         });
 
         const reqdStackOpts:DocumentOptions ={
-            title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
             _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
@@ -413,14 +414,14 @@ export class CurrentUserUtils {
     static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, {
         ...opts,
          _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15,
-        borderRounding: "5px", boxShadow: "0 0", isSystem: true
+        layout_borderRounding: "5px", layout_boxShadow: "0 0", isSystem: true
     }) as any as Doc
 
     // sets up the text container for the information contained within the mobile button
     static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, {
         ...opts,
         _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25,
-        backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, isSystem: true
+        backgroundColor: "rgba(0,0,0,0)", layout_borderRounding: "0", layout_boxShadow: "0 0", ignoreClick: true, isSystem: true
     }) as any as Doc
 
     // Sets up the title of the button
@@ -458,7 +459,7 @@ export class CurrentUserUtils {
     static setupSearcher(doc: Doc, field:string) {
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
                 dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDropAction: "embed", 
-                _lockedPosition: true, _type_collection: CollectionViewType.Schema, _searchDoc: true,  });
+                _lockedPosition: true, _type_collection: CollectionViewType.Schema  });
     }
 
     /// Initializes the panel of draggable tools that is opened from the left sidebar.
@@ -467,8 +468,8 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
         const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
-            title: "My Tools", isSystem: true, ignoreClick: true, boxShadow: "0 0",
-            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+            title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
+            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, 
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
     }
@@ -480,10 +481,10 @@ export class CurrentUserUtils {
         const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
         const newDashboard = `createNewDashboard()`;
 
-        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true,
              title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true };
         const reqdBtnScript = {onClick: newDashboard,}
-        const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+        const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
        
         const contextMenuScripts = [/*newDashboard*/] as string[];
         const contextMenuLabels = [/*"Create New Dashboard"*/]  as string[];
@@ -493,15 +494,15 @@ export class CurrentUserUtils {
         const childContextMenuLabels =  ["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
         const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
-            title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true,
+            title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
             targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
-            buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "embed",
+            layout_headerButton: newDashboardButton, childDropAction: "embed",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
             childContextMenuLabels:new List<string>(childContextMenuLabels),
             childContextMenuIcons:new List<string>(childContextMenuIcons),
-            explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
+            layout_explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
         };
         myDashboards = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
         if (Cast(myDashboards.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
@@ -523,19 +524,19 @@ export class CurrentUserUtils {
         
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
-            _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30,
+            _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _width: 30, _height: 30,
             title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: newFolder};
-        const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
+        const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
-            title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true,
-            isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, 
+            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true,
+            isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
             treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed",
             childContextMenuLabels: new List<string>(["Create new folder"]),
             childContextMenuIcons: new List<string>(["plus"]),
-            explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
+            layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
         };
         myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
         const childContextMenuScripts = [newFolder];
@@ -548,21 +549,21 @@ export class CurrentUserUtils {
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
-            title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true,
-            treeViewTruncateTitleWidth: 350, ignoreClick: true,  boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
+            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true,
+            treeViewTruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
-            explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
+            layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
         };
         const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
 
         const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
-        const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, 
+        const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, 
             title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", isSystem: true,
             toolTip: "Empty recently closed",};
-        const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
+        DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
 
-        if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton;
+        //if (recentlyClosed.layout_headerButton !== clearDocsButton) Doc.GetProto(recentlyClosed).layout_headerButton = clearDocsButton;
         
         if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
             recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
@@ -574,7 +575,7 @@ export class CurrentUserUtils {
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
-            boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true,
+            layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true,
             treeViewHideTitle: true, treeViewTruncateTitleWidth: 350
         };
         if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
@@ -582,14 +583,14 @@ export class CurrentUserUtils {
     }
 
     static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
-        ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
+        ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, layout_boxShadow: "0 0", _forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
         _lockedPosition: true, isSystem: true, flexDirection: "row"
     })
 
     static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
-        btnType: ButtonType.ToolButton, _forceActive: true, _hideContextMenu: true, 
-        _removeDropProperties: new List<string>([ "_hideContextMenu", "stayInCollection"]), 
+        btnType: ButtonType.ToolButton, _forceActive: true, _layout_hideContextMenu: true, 
+        _removeDropProperties: new List<string>([ "_layout_hideContextMenu", "stayInCollection"]), 
         _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts,
     })
 
@@ -609,8 +610,8 @@ export class CurrentUserUtils {
         ];
         const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
-            title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'embed',
-            childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
+            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDropAction: 'embed',
+            childDontRegisterViews: true, linearView_IsExpanded: true, linearView_Expandable: true, ignoreClick: true
         };
         reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
         reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
@@ -701,12 +702,12 @@ export class CurrentUserUtils {
             { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 20,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
             { title: "Num", icon:"",toolTip: "Frame Number (click to toggle edit mode)",btnType: ButtonType.TextButton,          expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
             { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 20,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
-            { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "Ink",     icon: "Ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    expertMode: false, toolType:DocumentType.INK,            funcs: {                                                                             linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
-            { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
-            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",    subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected
+            { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
+            { title: "Ink",     icon: "Ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    expertMode: false, toolType:DocumentType.INK,            funcs: {                                                                             linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
+            { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
+            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
+            { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
+            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected
         ];
     }
 
@@ -719,7 +720,7 @@ export class CurrentUserUtils {
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
             toolType: params.toolType, expertMode: params.expertMode,
-            _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true,
+            _stayInCollection: true, _layout_hideContextMenu: true, _lockedPosition: true,
             _removeDropProperties: new List<string>([ "_stayInCollection"]),
         };
         const reqdFuncs:{[key:string]:any} = {
@@ -731,7 +732,7 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
@@ -740,7 +741,7 @@ export class CurrentUserUtils {
             } else {
                 const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, dontUndo: true,
                     childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
-                    linearViewSubMenu: true, linearViewExpandable: true, };
+                    linearView_SubMenu: true, linearView_Expandable: true, };
                 const items = params.subMenu?.map(sub =>
                     this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
                 );
@@ -793,8 +794,8 @@ export class CurrentUserUtils {
             "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
             childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
             // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
-            _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
-            explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
+            _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
+            layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
         };
         
         DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
@@ -803,17 +804,17 @@ export class CurrentUserUtils {
     /// Import option on the left side button panel 
     static setupImportSidebar(doc: Doc, field:string) {   
         const reqdOpts:DocumentOptions = {
-            title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _layout_showTitle: "title", 
-            _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
-            childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
-            dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
+            title: "My Imports", _forceActive: true, ignoreClick: true, _layout_showTitle: "title", 
+            _stayInCollection: true, _layout_hideContextMenu: true, childLimitHeight: 0,
+            childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
+            dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go."
         };
         const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
     
         const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
-            _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
+            _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
             buttonText: "Import", icon: "upload", isSystem: true };
-        DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
+        DocUtils.AssignDocField(myImports, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
         return myImports;
     }
     /// Updates the UserDoc to have all required fields, docs, etc.  No changes should need to be
@@ -850,7 +851,7 @@ export class CurrentUserUtils {
         doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false);
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
-        doc.freezeChildren = "remove|add";
+        doc.treeViewFreezeChildren = "remove|add";
         this.setupLinkDocs(doc, linkDatabaseId);
         this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7a88ca991..612f16ce9 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -189,7 +189,7 @@ export class DocumentManager {
         while (
             containerDocContext.length &&
             containerDocContext[0]?.embedContainer &&
-            DocCast(containerDocContext[0].embedContainer)?.type_collection !== CollectionViewType.Docking &&
+            DocCast(containerDocContext[0].embedContainer)?._type_collection !== CollectionViewType.Docking &&
             (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0]))
         ) {
             containerDocContext = [Cast(containerDocContext[0].embedContainer, Doc, null), ...containerDocContext];
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index fb4a8985c..668de5408 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -517,7 +517,7 @@ export namespace DragManager {
 
             const target = document.elementFromPoint(e.x, e.y);
 
-            if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._noAutoscroll)) {
+            if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._freeform_noAutoPan)) {
                 const autoScrollHandler = () => {
                     target.dispatchEvent(
                         new CustomEvent<React.DragEvent>('dashDragAutoScroll', {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 41c2fa889..dbb05917e 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -134,7 +134,7 @@ export class LinkManager {
     public createlink_relationshipLists = () => {
         //create new lists for link relations and their associated colors if the lists don't already exist
         !Doc.UserDoc().link_relationshipList && (Doc.UserDoc().link_relationshipList = new List<string>());
-        !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>());
+        !Doc.UserDoc().link_ColorList && (Doc.UserDoc().link_ColorList = new List<string>());
         !Doc.UserDoc().link_relationshipSizes && (Doc.UserDoc().link_relationshipSizes = new List<number>());
     };
 
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index f83b6af0e..0e56ee1bc 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -3,6 +3,7 @@ import { NumCast } from '../../fields/Types';
 import { Doc, DocListCast } from '../../fields/Doc';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { Id } from '../../fields/FieldSymbols';
+import { CollectionViewType } from '../documents/DocumentTypes';
 
 export type Movement = {
     time: number;
@@ -89,7 +90,7 @@ export class TrackMovements {
         if (this.recordingFFViews === null) return;
 
         // so that the size comparisons are correct, we must filter to only the FFViews
-        const isFFView = (doc: Doc) => doc && 'type_collection' in doc && doc.type_collection === 'freeform';
+        const isFFView = (doc: Doc) => doc && doc._type_collection === CollectionViewType.Freeform;
         const tabbedFFViews = new Set<Doc>();
         for (const DashDoc of tabbedDocs) {
             if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 94dd010c3..50cf2226e 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -390,7 +390,7 @@ export class DashboardView extends React.Component {
             _width: 30,
             _height: 30,
             _stayInCollection: true,
-            _hideContextMenu: true,
+            _layout_hideContextMenu: true,
             title: 'New trail',
             toolTip: 'Create new trail',
             btnType: ButtonType.ClickButton,
@@ -415,16 +415,15 @@ export class DashboardView extends React.Component {
             childDropAction: 'embed',
             treeViewTruncateTitleWidth: 150,
             ignoreClick: true,
-            buttonMenu: true,
-            buttonMenuDoc: myTrailsBtn,
+            layout_headerButton: myTrailsBtn,
             contextMenuIcons: new List<string>(['plus']),
             contextMenuLabels: new List<string>(['Create New Trail']),
             _lockedPosition: true,
-            boxShadow: '0 0',
+            layout_boxShadow: '0 0',
             childDontRegisterViews: true,
             targetDropAction: 'same',
             isSystem: true,
-            explainer: 'All of the trails that you have created will appear here.',
+            layout_explainer: 'All of the trails that you have created will appear here.',
         };
         const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
         dashboardDoc.myTrails = new PrefetchProxy(myTrails);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 6fab4430d..db24229dc 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -128,8 +128,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
         isAnyChildContentActive = () => this._isAnyChildContentActive;
 
-        lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result;
-
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
         @computed public get annotationKey() {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 547d844ca..35b0b22a8 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -589,7 +589,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         <LinkPopup
                             key="popup"
                             showPopup={this._showLinkPopup}
-                            linkCreated={link => (link.layout_linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
+                            linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
                             linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
                             linkFrom={() => this.props.views().lastElement()?.rootDoc}
                         />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e4d50b143..528b82e3e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -332,7 +332,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
                         const ratio = dist / maxDist;
                         const radius = Math.min(1, ratio) * docMax;
-                        doc.borderRounding = `${radius}px`;
+                        doc.layout_borderRounding = `${radius}px`;
                     });
                 return false;
             }, // moveEvent
@@ -588,7 +588,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     let dW = docwidth * (dWin / refWidth);
                     let dH = docheight * (dHin / refHeight);
                     const scale = docView.props.ScreenToLocalTransform().Scale;
-                    const modifyNativeDim = (e.ctrlKey || doc.layout_forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
+                    const modifyNativeDim = (e.ctrlKey && doc.nativeDimModifiable) || (doc.layout_forceReflow && !dragBottom && !dragTop) || (doc.nativeHeightUnfrozen && (dragBottom || dragTop || e.ctrlKey));
                     if (nwidth && nheight) {
                         if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) {
                             docheight = (nheight / nwidth) * docwidth;
@@ -636,7 +636,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 }
                             }
                             if (!modifyNativeDim) {
-                                actualdH = Math.min((nheight / nwidth) * docwidth, actualdH);
+                                actualdH = (nheight / nwidth) * docwidth; //, actualdH);
                             }
                             doc._height = actualdH;
                         }
@@ -750,13 +750,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
         const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
         const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
-        const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
+        const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
         const hideOpenButton =
             hideDecorations ||
             seldocview.props.hideOpenButton ||
-            seldocview.rootDoc.hideOpenButton ||
-            SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) ||
+            seldocview.rootDoc.layout_hideOpenButton ||
+            SelectionManager.Views().some(docView => docView.rootDoc._stayInCollection || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) ||
             this._isRounding ||
             this._isRotating;
         const hideDeleteButton =
@@ -767,7 +767,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
-                return (docView.rootDoc.stayInCollection && !docView.rootDoc.timelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return (docView.rootDoc.stayInCollection && !docView.rootDoc._isTimelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
 
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
@@ -796,7 +796,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // Radius constants
         const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
-        const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding));
+        const borderRadius = numberValue(StrCast(seldocview.rootDoc.layout_borderRounding));
         const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 53c1f1018..fe42628cd 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -64,7 +64,7 @@ export class FilterPanel extends React.Component<filterProps> {
      * The current attributes selected to filter based on
      */
     @computed get activeFilters() {
-        return StrListCast(this.targetDoc?._docFilters);
+        return StrListCast(this.targetDoc?._childFilters);
     }
 
     /**
@@ -108,8 +108,8 @@ export class FilterPanel extends React.Component<filterProps> {
     @observable _chosenFacets = new ObservableMap<string, 'text' | 'checkbox' | 'slider' | 'range'>();
     @computed get activeFacets() {
         const facets = new Map<string, 'text' | 'checkbox' | 'slider' | 'range'>(this._chosenFacets);
-        StrListCast(this.targetDoc?._docFilters).map(filter => facets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox'));
-        setTimeout(() => StrListCast(this.targetDoc?._docFilters).map(action(filter => this._chosenFacets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox'))));
+        StrListCast(this.targetDoc?._childFilters).map(filter => facets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox'));
+        setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox'))));
         return facets;
     }
     /**
@@ -174,7 +174,7 @@ export class FilterPanel extends React.Component<filterProps> {
                         <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
                     </div>
                     <div className="filterBox-select-bool">
-                        <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._filterBoolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.filterBoolean)}>
+                        <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._childFilters_boolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.childFilters_boolean)}>
                             {['AND', 'OR'].map(bool => (
                                 <option value={bool} key={bool}>
                                     {bool}
@@ -202,7 +202,7 @@ export class FilterPanel extends React.Component<filterProps> {
                 return (
                     <input
                         placeholder={
-                            StrListCast(this.targetDoc._docFilters)
+                            StrListCast(this.targetDoc._childFilters)
                                 .find(filter => filter.split(':')[0] === facetHeader)
                                 ?.split(':')[1] ?? '-empty-'
                         }
@@ -218,7 +218,7 @@ export class FilterPanel extends React.Component<filterProps> {
                             <input
                                 style={{ width: 20, marginLeft: 20 }}
                                 checked={
-                                    StrListCast(this.targetDoc._docFilters)
+                                    StrListCast(this.targetDoc._childFilters)
                                         .find(filter => filter.split(':')[0] === facetHeader && filter.split(':')[1] == facetValue)
                                         ?.split(':')[2] === 'check'
                                 }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 141e99c66..0951bff22 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -140,7 +140,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 title: 'buttons',
                 _layout_autoHeight: true,
                 _yMargin: 5,
-                linearViewIsExpanded: true,
+                linearView_IsExpanded: true,
                 backgroundColor: 'white',
                 isSystem: true,
             });
@@ -148,7 +148,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 _width: 300,
                 _height: 25,
                 _layout_autoHeight: true,
-                linearViewIsExpanded: true,
+                linearView_IsExpanded: true,
                 flexDirection: 'column',
                 isSystem: true,
             });
@@ -1013,8 +1013,8 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 focus={emptyFunction}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
-                docRangeFilters={returnEmptyFilter}
-                docFilters={returnEmptyFilter}
+                childFiltersByRanges={returnEmptyFilter}
+                childFilters={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
             />
         );
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 1b309a6be..286d39943 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -43,7 +43,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     @observable private static _layoutTemplateString: Opt<string>;
     @observable private static _doc: Opt<Doc>;
     @observable private static _docTarget: Opt<Doc>;
-    @observable private static _docFilters: string[] = []; // filters
+    @observable private static _childFilters: string[] = []; // filters
     private static _savedState: Opt<LightboxSavedState>;
     private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
     @observable private static _future: Opt<Doc[]> = [];
@@ -58,7 +58,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             this.LightboxDoc.layout_fieldKey = this._savedState.layout_fieldKey;
         }
         if (!doc) {
-            this._docFilters && (this._docFilters.length = 0);
+            this._childFilters && (this._childFilters.length = 0);
             this._future = this._history = [];
             Doc.ActiveTool = InkTool.None;
             MainView.Instance._exploreMode = false;
@@ -134,7 +134,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     @action
     public static SetCookie(cookie: string) {
         if (this.LightboxDoc && cookie) {
-            this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`);
+            this._childFilters = (f => (this._childFilters ? [this._childFilters.push(f) as any, this._childFilters][1] : [f]))(`cookies:${cookie}:provide`);
         }
     }
     public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => {
@@ -148,7 +148,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             layoutTemplate
         );
     };
-    docFilters = () => LightboxView._docFilters || [];
+    childFilters = () => LightboxView._childFilters || [];
     addDocTab = LightboxView.AddDocTab;
     @action public static Next() {
         const doc = LightboxView._doc!;
@@ -261,8 +261,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                             renderDepth={0}
                             rootSelected={returnTrue}
                             docViewPath={returnEmptyDoclist}
-                            docFilters={this.docFilters}
-                            docRangeFilters={returnEmptyFilter}
+                            childFilters={this.childFilters}
+                            childFiltersByRanges={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
                             addDocument={undefined}
                             removeDocument={undefined}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8e112dddb..b0e992cb6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -608,8 +608,8 @@ export class MainView extends React.Component {
                     focus={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
             </div>
@@ -637,8 +637,8 @@ export class MainView extends React.Component {
                     focus={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     suppressSetHeight={true}
                     renderDepth={this._hideUI ? 0 : -1}
@@ -736,8 +736,8 @@ export class MainView extends React.Component {
                         focus={emptyFunction}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-                        docFilters={returnEmptyFilter}
-                        docRangeFilters={returnEmptyFilter}
+                        childFilters={returnEmptyFilter}
+                        childFiltersByRanges={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                     />
                 </div>
@@ -767,8 +767,8 @@ export class MainView extends React.Component {
                     isContentActive={returnTrue}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     scriptContext={this}
                 />
@@ -903,8 +903,8 @@ export class MainView extends React.Component {
                     renderDepth={0}
                     focus={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
                 {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 6c36d39b9..a4a2c1df9 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -126,7 +126,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                           if (!e.aborted && e.linkDocument) {
                               Doc.GetProto(e.linkDocument).link_relationship = 'cropped image';
                               Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title;
-                              Doc.GetProto(e.linkDocument).layout_linkDisplay = false;
+                              Doc.GetProto(e.linkDocument).link_displayLine = false;
                           }
                       },
                   });
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 59865cba3..339507f65 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -228,8 +228,8 @@ export class OverlayView extends React.Component {
                             docViewPath={returnEmptyDoclist}
                             addDocTab={DocumentViewInternal.addDocTabFunc}
                             pinToPres={emptyFunction}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
+                            childFilters={returnEmptyFilter}
+                            childFiltersByRanges={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
                         />
                     </div>
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 3ad28c418..749eb08a2 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -56,8 +56,8 @@ export default class Palette extends React.Component<PaletteProps> {
                             styleProvider={returnEmptyString}
                             whenChildContentsActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
+                            childFilters={returnEmptyFilter}
+                            childFiltersByRanges={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
                         />
                         <div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 76828a576..11b89fd69 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -133,14 +133,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     const containerDoc = dv.rootDoc;
                     //containerDoc.followAllLinks =
                     // containerDoc.noShadow =
-                    // containerDoc.disableDocBrushing =
+                    // containerDoc.layout_disableBrushing =
                     // containerDoc._forceActive =
                     //containerDoc._freeform_fitContentsToBox =
                     containerDoc._isLightbox = !containerDoc._isLightbox;
                     //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
                     const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
                     //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
-                    containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
+                    containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.link_displayLine = false)));
                 });
             }
         );
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 7b21629da..3e69bcba6 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -19,8 +19,8 @@ type PropertiesDocBacklinksSelectorProps = {
 @observer
 export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDocBacklinksSelectorProps> {
     getOnClick = (link: Doc) => {
-        const linkSource = this.props.Document;
-        const other = LinkManager.getOppositeAnchor(link, linkSource);
+        const linkAnchor = this.props.Document;
+        const other = LinkManager.getOppositeAnchor(link, linkAnchor);
         const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other;
 
         if (otherdoc) {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 00e077f96..1f2e21dd5 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -276,8 +276,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         PanelHeight={panelHeight}
                         focus={emptyFunction}
                         ScreenToLocalTransform={this.getTransform}
-                        docFilters={returnEmptyFilter}
-                        docRangeFilters={returnEmptyFilter}
+                        childFilters={returnEmptyFilter}
+                        childFiltersByRanges={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                         addDocument={returnFalse}
                         moveDocument={undefined}
@@ -941,26 +941,26 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     /**
-     * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
+     * Updates this.filterDoc's currentFilter and saves the childFilters on the currentFilter
      */
     updateFilterDoc = (doc: Doc) => {
         if (this.selectedDoc) {
             if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
-            const savedDocFilters = doc._docFiltersList;
-            const currentDocFilters = this.selectedDoc._docFilters;
-            this.selectedDoc._docFilters = new List<string>();
-            (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+            const savedDocFilters = doc._childFiltersList;
+            const currentDocFilters = this.selectedDoc._childFilters;
+            this.selectedDoc._childFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._childFiltersList = currentDocFilters;
             this.selectedDoc.currentFilter = doc;
-            doc._docFiltersList = new List<string>();
-            this.selectedDoc._docFilters = savedDocFilters;
+            doc._childFiltersList = new List<string>();
+            this.selectedDoc._childFilters = savedDocFilters;
 
-            const savedDocRangeFilters = doc._docRangeFiltersList;
-            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-            this.selectedDoc._docRangeFilters = new List<string>();
-            (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+            const savedDocRangeFilters = doc._childFiltersByRangesList;
+            const currentDocRangeFilters = this.selectedDoc._childFiltersByRanges;
+            this.selectedDoc._childFiltersByRanges = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._childFiltersByRangesList = currentDocRangeFilters;
             this.selectedDoc.currentFilter = doc;
-            doc._docRangeFiltersList = new List<string>();
-            this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+            doc._childFiltersByRangesList = new List<string>();
+            this.selectedDoc._childFiltersByRanges = savedDocRangeFilters;
         }
     };
 
@@ -1117,7 +1117,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             Doc.GetProto(LinkManager.currentLink).link_relationship = value;
             const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
             const linkRelationshipSizes = NumListCast(Doc.UserDoc().link_relationshipSizes);
-            const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
+            const linkColorList = StrListCast(Doc.UserDoc().link_ColorList);
 
             // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
             if (!linkRelationshipList?.includes(value)) {
@@ -1328,8 +1328,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline">
                                         <p>Show link</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.layout_linkDisplay ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_linkDisplay')}
+                                            style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
@@ -1338,8 +1338,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
                                         <p>Auto-move anchors</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.layout_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_autoMoveAnchors')}
+                                            style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
@@ -1348,8 +1348,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
                                         <p>Display arrow</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.layout_linkDisplayArrow ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_linkDisplayArrow')}
+                                            style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 498e2ccba..db273cc88 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -63,11 +63,11 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         return Array.from(keys.keys()).sort();
     }
     get filtersKey() {
-        return '_' + this.sidebarKey + '_docFilters';
+        return '_' + this.sidebarKey + '_childFilters';
     }
 
     anchorMenuClick = (anchor: Doc) => {
-        const startup = StrListCast(this.props.rootDoc.docFilters)
+        const startup = StrListCast(this.props.rootDoc.childFilters)
             .map(filter => filter.split(':')[0])
             .join(' ');
         const target = Docs.Create.TextDocument(startup, {
@@ -83,9 +83,9 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
         const link = DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' });
-        link && (link.layout_linkDisplay = false);
+        link && (link.link_displayLine = false);
 
-        const taggedContent = this.docFilters()
+        const taggedContent = this.childFilters()
             .filter(data => data.split(':')[0])
             .map(data => {
                 const key = data.split(':')[0];
@@ -177,7 +177,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey);
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
-    docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
+    childFilters = () => [...StrListCast(this.props.layoutDoc._childFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
     layout_showTitle = () => 'title';
     setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     sortByLinkAnchorY = (a: Doc, b: Doc) => {
@@ -238,7 +238,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         ref={this._stackRef}
                         PanelHeight={this.panelHeight}
                         PanelWidth={this.panelWidth}
-                        docFilters={this.docFilters}
+                        childFilters={this.childFilters}
                         sortFunc={this.sortByLinkAnchorY}
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index cf0177f4b..9199bf931 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -115,7 +115,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             allSorts[TreeSort.None] = { color: 'darkgray', label: '\u00A0\u00A0\u00A0' };
             return allSorts;
         case StyleProp.Highlighting:
-            if (doc && !doc.disableDocBrushing && !props?.disableDocBrushing) {
+            if (doc && !doc.layout_disableBrushing && !props?.disableBrushing) {
                 const selected = SelectionManager.Views().some(dv => dv.rootDoc === doc);
                 const highlightIndex = Doc.isBrushedHighlightedDegree(doc) || (selected ? Doc.DocBrushStatus.selfBrushed : 0);
                 const highlightColor = ['transparent', 'rgb(68, 118, 247)', selected ? 'black' : 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
@@ -166,7 +166,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             if (!backColor) return undefined;
             return lightOrDark(backColor);
         case StyleProp.BorderRounding:
-            return StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.borderRounding, doc?._type_collection === CollectionViewType.Pile ? '50%' : ''));
+            return StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.layout_borderRounding, doc?._type_collection === CollectionViewType.Pile ? '50%' : ''));
         case StyleProp.BorderPath:
             const borderPath = Doc.IsComicStyle(doc) &&
                 props?.renderDepth &&
@@ -235,38 +235,37 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         }
         case StyleProp.BoxShadow: {
             if (!doc || opacity() === 0 || doc.noShadow) return undefined; // if it's not visible, then no shadow)
-            if (doc.boxShadow === 'standard') return Shadows.STANDARD_SHADOW;
+            if (doc.layout_boxShadow === 'standard') return Shadows.STANDARD_SHADOW;
             if (IsFollowLinkScript(doc?.onClick) && LinkManager.Links(doc).length && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em');
             switch (doc?.type) {
                 case DocumentType.COL:
                     return StrCast(
-                        doc?.boxShadow,
+                        doc?.layout_borderRounding,
                         doc?._type_collection === CollectionViewType.Pile
                             ? '4px 4px 10px 2px'
                             : lockedPosition() || doc?._isGroup || docProps?.LayoutTemplateString
                             ? undefined // groups have no drop shadow -- they're supposed to be "invisible".  LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
-                            : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, '0.2vw 0.2vw 0.8vw')}`
+                            : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.layout_borderRounding, '0.2vw 0.2vw 0.8vw')}`
                     );
 
                 case DocumentType.LABEL:
                     if (doc?.annotationOn !== undefined) return 'black 2px 2px 1px';
                 default:
                     return doc.z
-                        ? `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
+                        ? `#9c9396  ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
                         : props?.docViewPath().lastElement()?.rootDoc._freeform_useClusters
-                        ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1
-                        ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : lockedPosition()
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
-                        : StrCast(doc.boxShadow, '');
+                        : StrCast(doc.layout_boxShadow, '');
             }
         }
         case StyleProp.PointerEvents:
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString;
             if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.();
             if (MainView.Instance._exploreMode || doc?.layout_unrendered) return isInk ? 'visiblePainted' : 'all';
-            if (doc?.pointerEvents) return StrCast(doc.pointerEvents);
             if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents);
             if (props?.pointerEvents?.() === 'none') return 'none';
             if (opacity() === 0) return 'none';
@@ -284,9 +283,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             };
             const filter = () => {
                 const showFilterIcon =
-                    StrListCast(doc?._docFilters).length || StrListCast(doc?._docRangeFilters).length
+                    StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length
                         ? '#18c718bd' //'hasFilter'
-                        : docProps?.docFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || docProps?.docRangeFilters().length
+                        : docProps?.childFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || docProps?.childFiltersByRanges().length
                         ? 'orange' //'inheritsFilter'
                         : undefined;
                 return !showFilterIcon ? null : (
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 814a95ab8..62f47ba71 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -116,8 +116,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                     styleProvider={DefaultStyleProvider}
                     setHeight={returnFalse}
                     docViewPath={returnEmptyDoclist}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     rootSelected={returnFalse}
                     onCheckedClick={this.scriptField}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index dc430667b..fc3f713ce 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -130,8 +130,8 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
                     renderDepth={0}
                     focus={emptyFunction}
                     whenChildContentsActiveChanged={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
             </div>
@@ -277,16 +277,16 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     _saveFilterCommand = {
         params: ['target'],
         title: 'save filter',
-        script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']); 
+        script: `self.target._childFilters = compareLists(self['target-childFilters'],self.target._childFilters) ? undefined : copyField(self['target-childFilters']); 
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => {
-            this.target._docFilters = undefined;
+            this.target._childFilters = undefined;
             this.target._searchFilterDocs = undefined;
         }),
         initialize: (button: Doc) => {
             const activeDash = Doc.ActiveDashboard;
             if (activeDash) {
-                button['target-docFilters'] = (Doc.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
+                button['target-childFilters'] = (Doc.MySearcher._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._childFilters || activeDash._childFilters) as any as ObjectField) : undefined;
                 button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
             }
         },
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index fc265e2b9..675f23970 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -201,13 +201,17 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     };
 
     styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
-        if (property === StyleProp.BoxShadow && doc && DragManager.docsBeingDragged.includes(doc)) {
-            return `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}`;
-        }
-        if (property === StyleProp.Opacity && doc) {
-            if (this.props.childOpacity) {
-                return this.props.childOpacity();
-            }
+        switch (property) {
+            case StyleProp.BoxShadow:
+                if (doc && DragManager.docsBeingDragged.includes(doc)) {
+                    return `#9c9396 ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}`;
+                }
+                break;
+            case StyleProp.Opacity:
+                if (doc && this.props.childOpacity) {
+                    return this.props.childOpacity();
+                }
+                break;
         }
         return this.props.styleProvider?.(doc, props, property);
     };
@@ -251,11 +255,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={noteTakingDocTransform}
                 focus={this.focusDocument}
-                docFilters={this.childDocFilters}
+                childFilters={this.childDocFilters}
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 hideResizeHandles={this.props.childHideResizeHandles?.()}
                 hideTitle={this.props.childHideTitle?.()}
-                docRangeFilters={this.childDocRangeFilters}
+                childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
                 addDocument={this.props.addDocument}
                 moveDocument={this.props.moveDocument}
@@ -603,42 +607,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         return eles;
     }
 
-    @computed get buttonMenu() {
-        const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
-        if (menuDoc) {
-            const width = NumCast(menuDoc._width, 30);
-            const height = NumCast(menuDoc._height, 30);
-            return (
-                <div className="buttonMenu-docBtn" style={{ width, height }}>
-                    <DocumentView
-                        Document={menuDoc}
-                        DataDoc={menuDoc}
-                        isContentActive={this.props.isContentActive}
-                        isDocumentActive={returnTrue}
-                        addDocument={this.props.addDocument}
-                        moveDocument={this.props.moveDocument}
-                        addDocTab={this.props.addDocTab}
-                        pinToPres={emptyFunction}
-                        rootSelected={this.props.isSelected}
-                        removeDocument={this.props.removeDocument}
-                        ScreenToLocalTransform={Transform.Identity}
-                        PanelWidth={() => 35}
-                        PanelHeight={() => 35}
-                        renderDepth={this.props.renderDepth}
-                        focus={emptyFunction}
-                        styleProvider={this.props.styleProvider}
-                        docViewPath={returnEmptyDoclist}
-                        whenChildContentsActiveChanged={emptyFunction}
-                        bringToFront={emptyFunction}
-                        docFilters={this.props.docFilters}
-                        docRangeFilters={this.props.docRangeFilters}
-                        searchFilterDocs={this.props.searchFilterDocs}
-                    />
-                </div>
-            );
-        }
-    }
-
     @computed get nativeWidth() {
         return Doc.NativeWidth(this.layoutDoc);
     }
@@ -658,35 +626,25 @@ export class CollectionNoteTakingView extends CollectionSubView() {
 
     render() {
         TraceMobx();
-        const buttonMenu = this.rootDoc.buttonMenu;
-        const noviceExplainer = StrCast(this.rootDoc.explainer);
         return (
-            <>
-                {buttonMenu || noviceExplainer ? (
-                    <div className="documentButtonMenu" key="buttons">
-                        {buttonMenu ? this.buttonMenu : null}
-                        {Doc.UserDoc().noviceMode && noviceExplainer ? <div className="documentExplanation">{noviceExplainer}</div> : null}
-                    </div>
-                ) : null}
-                <div
-                    className="collectionNoteTakingView"
-                    ref={this.createRef}
-                    key="notes"
-                    style={{
-                        overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
-                        background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
-                        pointerEvents: this.backgroundEvents ? 'all' : undefined,
-                    }}
-                    onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
-                    onPointerLeave={action(e => (this.docsDraggedRowCol.length = 0))}
-                    onPointerMove={e => e.buttons && this.onPointerMove(false, e.clientX, e.clientY)}
-                    onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)}
-                    onDrop={this.onExternalDrop.bind(this)}
-                    onContextMenu={this.onContextMenu}
-                    onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}>
-                    {this.renderedSections}
-                </div>
-            </>
+            <div
+                className="collectionNoteTakingView"
+                ref={this.createRef}
+                key="notes"
+                style={{
+                    overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
+                    background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
+                    pointerEvents: this.backgroundEvents ? 'all' : undefined,
+                }}
+                onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
+                onPointerLeave={action(e => (this.docsDraggedRowCol.length = 0))}
+                onPointerMove={e => e.buttons && this.onPointerMove(false, e.clientX, e.clientY)}
+                onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)}
+                onDrop={this.onExternalDrop.bind(this)}
+                onContextMenu={this.onContextMenu}
+                onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}>
+                {this.renderedSections}
+            </div>
         );
     }
 }
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index bbc91aa6e..57d9bbb49 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -23,7 +23,7 @@ export class CollectionPileView extends CollectionSubView() {
 
     componentDidMount() {
         if (this.layoutEngine() !== computePassLayout.name && this.layoutEngine() !== computeStarburstLayout.name) {
-            this.Document._pileLayoutEngine = computePassLayout.name;
+            this.Document._freeform_pileEngine = computePassLayout.name;
         }
         this._originalChrome = this.layoutDoc._chromeHidden;
         this.layoutDoc._chromeHidden = true;
@@ -33,7 +33,7 @@ export class CollectionPileView extends CollectionSubView() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
-    layoutEngine = () => StrCast(this.Document._pileLayoutEngine);
+    layoutEngine = () => StrCast(this.Document._freeform_pileEngine);
 
     @undoBatch
     addPileDoc = (doc: Doc | Doc[]) => {
@@ -73,28 +73,29 @@ export class CollectionPileView extends CollectionSubView() {
 
     // toggles the pileup between starburst to compact
     toggleStarburst = action(() => {
+        this.layoutDoc._freeform_scale = undefined;
         if (this.layoutEngine() === computeStarburstLayout.name) {
             if (this.rootDoc[Width]() !== NumCast(this.rootDoc._starburstDiameter, 500)) {
                 this.rootDoc._starburstDiameter = this.rootDoc[Width]();
             }
             const defaultSize = 110;
-            this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2;
-            this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
-            this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
-            this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
+            this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
+            this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
+            this.layoutDoc._width = NumCast(this.layoutDoc._freeform_pileWidth, defaultSize);
+            this.layoutDoc._height = NumCast(this.layoutDoc._freeform_pileHeight, defaultSize);
             DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
             this.layoutDoc._freeform_panX = 0;
             this.layoutDoc._freeform_panY = -10;
-            this.props.Document._pileLayoutEngine = computePassLayout.name;
+            this.props.Document._freeform_pileEngine = computePassLayout.name;
         } else {
             const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500);
             this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - defaultSize / 2;
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - defaultSize / 2;
-            this.layoutDoc._starburstPileWidth = this.layoutDoc[Width]();
-            this.layoutDoc._starburstPileHeight = this.layoutDoc[Height]();
+            this.layoutDoc._freeform_pileWidth = this.layoutDoc[Width]();
+            this.layoutDoc._freeform_pileHeight = this.layoutDoc[Height]();
             this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
             this.layoutDoc._width = this.layoutDoc._height = defaultSize;
-            this.props.Document._pileLayoutEngine = computeStarburstLayout.name;
+            this.props.Document._freeform_pileEngine = computeStarburstLayout.name;
         }
     });
 
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index b131d38d8..9d5cb257a 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -404,15 +404,15 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             docAnchor ??
             Docs.Create.LabelDocument({
                 title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
-                _minFontSize: 12,
-                _maxFontSize: 24,
+                _label_minFontSize: 12,
+                _label_maxFontSize: 24,
                 _stayInCollection: true,
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
                 layout_hideLinkButton: true,
                 onClick: FollowLinkScript(),
                 annotationOn: rootDoc,
-                _timelineLabel: true,
-                borderRounding: anchorEndTime === undefined ? '100%' : undefined,
+                _isTimelineLabel: true,
+                layout_borderRounding: anchorEndTime === undefined ? '100%' : undefined,
             });
         Doc.GetProto(anchor)[startTag] = anchorStartTime;
         Doc.GetProto(anchor)[endTag] = anchorEndTime;
@@ -752,7 +752,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             if (timelineOnly) {
                 if (!left && time !== undefined && time <= NumCast(anchor[this.props.startTag])) time = undefined;
                 Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true);
-                if (!left) Doc.SetInPlace(anchor, 'borderRounding', time !== undefined ? undefined : '100%', true);
+                if (!left) Doc.SetInPlace(anchor, 'layout_borderRounding', time !== undefined ? undefined : '100%', true);
             } else {
                 anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time;
             }
@@ -820,8 +820,8 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     focus={focusFunc}
                     isContentActive={returnFalse}
                     searchFilterDocs={returnEmptyDoclist}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     rootSelected={returnFalse}
                     onClick={script}
                     onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 56f53afe6..3c0d8cbc3 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -346,11 +346,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={stackedDocTransform}
                 focus={this.focusDocument}
-                docFilters={this.childDocFilters}
+                childFilters={this.childDocFilters}
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 hideResizeHandles={this.props.childHideResizeHandles?.()}
                 hideTitle={this.props.childHideTitle?.()}
-                docRangeFilters={this.childDocRangeFilters}
+                childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
                 xPadding={NumCast(this.layoutDoc._childXPadding, this.props.childXPadding)}
                 yPadding={NumCast(this.layoutDoc._childYPadding, this.props.childYPadding)}
@@ -650,7 +650,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
 
     return35 = () => 35;
     @computed get buttonMenu() {
-        const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        const menuDoc: Doc = Cast(this.rootDoc.layout_headerButton, Doc, null);
         // TODO:glr Allow support for multiple buttons
         if (menuDoc) {
             const width: number = NumCast(menuDoc._width, 30);
@@ -679,8 +679,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-                        docFilters={this.props.docFilters}
-                        docRangeFilters={this.props.docRangeFilters}
+                        childFilters={this.props.childFilters}
+                        childFiltersByRanges={this.props.childFiltersByRanges}
                         searchFilterDocs={this.props.searchFilterDocs}
                     />
                 </div>
@@ -711,7 +711,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             contents: '+ ADD A GROUP',
         };
         const buttonMenu = this.rootDoc.buttonMenu;
-        const noviceExplainer = this.rootDoc.explainer;
+        const noviceExplainer = this.rootDoc.layout_explainer;
         return (
             <>
                 {buttonMenu || noviceExplainer ? (
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 967d1d40e..d1b7f6ff6 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -28,8 +28,8 @@ export function CollectionSubView<X>(moreProps?: X) {
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
         protected _mainCont?: HTMLDivElement;
-        @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
-        @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
+        @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
         protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
             //used for stacking and masonry view
             this.dropDisposer?.();
@@ -81,13 +81,13 @@ export function CollectionSubView<X>(moreProps?: X) {
         get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
-        collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters);
-        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []);
+        collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._childFilters);
+        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._childFiltersByRanges, listSpec('string'), []);
         // child filters apply to the descendants of the documents in this collection
-        childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
+        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.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
-        childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
+        unrecursiveDocFilters = () => [...(this.props.childFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
+        childDocRangeFilters = () => [...(this.props.childFiltersByRanges?.() || []), ...this.collectionRangeDocFilters()];
         searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
@@ -109,20 +109,20 @@ export function CollectionSubView<X>(moreProps?: X) {
             const childDocs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this.props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc);
 
             const childDocFilters = this.childDocFilters();
-            const docRangeFilters = this.childDocRangeFilters();
+            const childFiltersByRanges = this.childDocRangeFilters();
             const searchDocs = this.searchFilterDocs();
-            if (this.props.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !docRangeFilters.length && !searchDocs.length)) {
+            if (this.props.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
             }
 
             const docsforFilter: Doc[] = [];
             childDocs.forEach(d => {
                 // dragging facets
-                const dragged = this.props.docFilters?.().some(f => f.includes(Utils.noDragsDocFilter));
+                const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragsDocFilter));
                 if (dragged && DragManager.docsBeingDragged.includes(d)) return false;
-                let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, this.props.Document).length > 0;
+                let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.props.Document).length > 0;
                 if (notFiltered) {
-                    notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, this.props.Document).length > 0;
+                    notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.props.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];
@@ -131,13 +131,14 @@ export function CollectionSubView<X>(moreProps?: X) {
                         let subDocs = [...DocListCast(data), ...DocListCast(side)];
                         if (subDocs.length > 0) {
                             let newarray: Doc[] = [];
-                            notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, d).length);
+                            notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, childFiltersByRanges, d).length);
                             while (subDocs.length > 0 && !notFiltered) {
                                 newarray = [];
                                 subDocs.forEach(t => {
                                     const fieldKey = Doc.LayoutFieldKey(t);
                                     const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name);
-                                    notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, d).length));
+                                    notFiltered =
+                                        notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length));
                                     DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                                     annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
                                 });
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 49a90d828..60e6815e5 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -125,8 +125,8 @@ export class CollectionTimeView extends CollectionSubView() {
 
     goTo = (prevFilterIndex: number) => {
         this.layoutDoc._pivotField = this.layoutDoc['_prevPivotFields' + prevFilterIndex];
-        this.layoutDoc._docFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocFilter' + prevFilterIndex] as ObjectField);
-        this.layoutDoc._docRangeFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocRangeFilters' + prevFilterIndex] as ObjectField);
+        this.layoutDoc._childFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocFilter' + prevFilterIndex] as ObjectField);
+        this.layoutDoc._childFiltersByRanges = ObjectField.MakeCopy(this.layoutDoc['_prevDocRangeFilters' + prevFilterIndex] as ObjectField);
         this.layoutDoc._prevFilterIndex = prevFilterIndex;
     };
 
@@ -136,7 +136,7 @@ export class CollectionTimeView extends CollectionSubView() {
         if (prevFilterIndex > 0) {
             this.goTo(prevFilterIndex - 1);
         } else {
-            this.layoutDoc._docFilters = new List([]);
+            this.layoutDoc._childFilters = new List([]);
         }
     };
 
@@ -145,7 +145,7 @@ export class CollectionTimeView extends CollectionSubView() {
             <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
                 <CollectionFreeFormView
                     {...this.props}
-                    engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
+                    engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
                     fitContentsToBox={returnTrue}
                     childClickScript={this._childClickedScript}
                     viewDefDivClick={this.layoutEngine() === computeTimelineLayout.name ? undefined : this._viewDefDivClick}
@@ -276,12 +276,12 @@ export class CollectionTimeView extends CollectionSubView() {
 ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
     const pivotField = StrCast(pivotDoc._pivotField, 'author');
     let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
-    const originalFilter = StrListCast(ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField));
-    pivotDoc['_prevDocFilter' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField);
-    pivotDoc['_prevDocRangeFilters' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docRangeFilters as ObjectField);
+    const originalFilter = StrListCast(ObjectField.MakeCopy(pivotDoc._childFilters as ObjectField));
+    pivotDoc['_prevDocFilter' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._childFilters as ObjectField);
+    pivotDoc['_prevDocRangeFilters' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._childFiltersByRanges as ObjectField);
     pivotDoc['_prevPivotFields' + prevFilterIndex] = pivotField;
     pivotDoc._prevFilterIndex = ++prevFilterIndex;
-    pivotDoc._docFilters = new List();
+    pivotDoc._childFilters = new List();
     setTimeout(
         action(() => {
             const filterVals = bounds.payload as string[];
@@ -292,7 +292,7 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou
                     pivotDoc._pivotField = filterVals[0];
                 }
             }
-            const newFilters = StrListCast(pivotDoc._docFilters);
+            const newFilters = StrListCast(pivotDoc._childFilters);
             if (newFilters.length && originalFilter.length && newFilters.lastElement() === originalFilter.lastElement()) {
                 pivotDoc._prevFilterIndex = --prevFilterIndex;
                 pivotDoc['_prevDocFilter' + prevFilterIndex] = undefined;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 969658238..ed1e0c067 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -240,8 +240,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 PanelHeight={this.documentTitleHeight}
                 NativeDimScaling={returnOne}
                 onKey={this.onKey}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
+                childFilters={returnEmptyFilter}
+                childFiltersByRanges={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
                 addDocument={returnFalse}
                 moveDocument={returnFalse}
@@ -314,12 +314,12 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     @computed get noviceExplainer() {
-        return !Doc.noviceMode || !this.rootDoc.explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.explainer)} </div>;
+        return !Doc.noviceMode || !this.rootDoc.layout_explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.layout_explainer)} </div>;
     }
 
     return35 = () => 35;
     @computed get buttonMenu() {
-        const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        const menuDoc = Cast(this.rootDoc.layout_headerButton, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
         return !menuDoc ? null : (
             <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
@@ -343,8 +343,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
-                    docFilters={this.props.docFilters}
-                    docRangeFilters={this.props.docRangeFilters}
+                    childFilters={this.props.childFilters}
+                    childFiltersByRanges={this.props.childFiltersByRanges}
                     searchFilterDocs={this.props.searchFilterDocs}
                 />
             </div>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 710e33b1c..75e4e8abf 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -432,8 +432,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     PanelWidth={this.PanelWidth}
                     PanelHeight={this.PanelHeight}
                     styleProvider={DefaultStyleProvider}
-                    docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
-                    docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                    childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+                    childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
                     searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
                     addDocument={undefined}
                     removeDocument={this.remDocTab}
@@ -602,8 +602,8 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     styleProvider={TabMinimapView.miniStyleProvider}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
-                    docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
-                    docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                    childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+                    childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
                     searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
                     fitContentsToBox={returnTrue}
                 />
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ca9471f6f..9039875c1 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -119,7 +119,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return 'TreeView(' + this.props.document.title + ')';
     } // this makes mobx trace() statements more descriptive
     get defaultExpandedView() {
-        return this.doc.type_collection === CollectionViewType.Docking
+        return this.doc._type_collection === CollectionViewType.Docking
             ? this.fieldKey
             : this.props.treeView.dashboardMode
             ? this.fieldKey
@@ -388,7 +388,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         };
         const addDoc = inside ? localAdd : parentAddDoc;
         const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
-        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.freezeChildren).includes('add')) || forceAdd;
+        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd;
         if (canAdd) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
             const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false));
@@ -714,7 +714,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : '');
         const embeddings = () => (this.props.treeView.dashboardMode ? '' : 'embeddings');
         const fields = () => (Doc.noviceMode ? '' : 'fields');
-        const layout = Doc.noviceMode || this.doc.type_collection === CollectionViewType.Docking ? [] : ['layout'];
+        const layout = Doc.noviceMode || this.doc._type_collection === CollectionViewType.Docking ? [] : ['layout'];
         return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [embeddings(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
@@ -732,7 +732,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
             <>
                 {customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */}
-                {this.doc.hideContextMenu ? null : (
+                {this.doc._layout_hideContextMenu ? null : (
                     <FontAwesomeIcon
                         title="context menu"
                         key="bars"
@@ -772,7 +772,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 ? []
                 : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
                 ? [openEmbedding, makeFolder]
-                : this.doc.type_collection === CollectionViewType.Docking
+                : this.doc._type_collection === CollectionViewType.Docking
                 ? []
                 : [deleteItem, openEmbedding, focusDoc]),
         ];
@@ -930,13 +930,13 @@ export class TreeView extends React.Component<TreeViewProps> {
                 focus={this.refocus}
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                 bringToFront={emptyFunction}
-                disableDocBrushing={this.props.treeView.props.disableDocBrushing}
+                disableBrushing={this.props.treeView.props.disableBrushing}
                 hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
                 dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
                 xPadding={NumCast(this.props.treeView.props.Document.childXPadding, this.props.treeView.props.childXPadding)}
                 yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
+                childFilters={returnEmptyFilter}
+                childFiltersByRanges={returnEmptyFilter}
                 searchFilterDocs={returnEmptyDoclist}
             />
         );
@@ -1010,8 +1010,8 @@ export class TreeView extends React.Component<TreeViewProps> {
                     treeViewDoc={this.props.treeView?.props.Document}
                     rootSelected={returnTrue}
                     docViewPath={this.props.treeView.props.docViewPath}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     addDocument={this.props.addDocument}
                     moveDocument={this.move}
@@ -1021,7 +1021,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)}
                     addDocTab={this.props.addDocTab}
                     pinToPres={this.props.treeView.props.pinToPres}
-                    disableDocBrushing={this.props.treeView.props.disableDocBrushing}
+                    disableBrushing={this.props.treeView.props.disableBrushing}
                     bringToFront={returnFalse}
                     scriptContext={this}
                 />
@@ -1211,7 +1211,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         onCheckedClick={onCheckedClick}
                         onChildClick={onChildClick}
                         renderDepth={renderDepth}
-                        removeDoc={StrCast(treeViewParent.freezeChildren).includes('remove') ? undefined : remove}
+                        removeDoc={StrCast(treeViewParent.treeViewFreezeChildren).includes('remove') ? undefined : remove}
                         addDocument={addDocument}
                         styleProvider={styleProvider}
                         panelWidth={rowWidth}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 6202dcacc..f1d98d22a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -59,7 +59,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             0
         ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
         setTimeout(
-            action(() => (!LinkDocs.length || !linkDoc.layout_linkDisplay) && (this._opacity = 0.05)),
+            action(() => (!LinkDocs.length || !linkDoc.link_displayLine) && (this._opacity = 0.05)),
             750
         ); // this will unhighlight the link line.
         const a = A.ContentDiv.getBoundingClientRect();
@@ -77,7 +77,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.link_anchor_2 as Doc)[Id])).lastElement();
         if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
         if (!targetAhyperlink) {
-            if (linkDoc.layout_autoMoveAnchors) {
+            if (linkDoc.link_autoMoveAnchors) {
                 linkDoc.link_anchor_1_x = ((apt.point.x - aleft) / awidth) * 100;
                 linkDoc.link_anchor_1_y = ((apt.point.y - atop) / aheight) * 100;
             }
@@ -92,7 +92,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             else linkDoc.opacity = 1;
         }
         if (!targetBhyperlink) {
-            if (linkDoc.layout_autoMoveAnchors) {
+            if (linkDoc.link_autoMoveAnchors) {
                 linkDoc.link_anchor_2_x = ((bpt.point.x - bleft) / bwidth) * 100;
                 linkDoc.link_anchor_2_y = ((bpt.point.y - btop) / bheight) * 100;
             }
@@ -247,7 +247,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
         const linkRelationship = Field.toString(link?.link_relationship as any as Field); //get string representing relationship
         const linkRelationshipList = Doc.UserDoc().link_relationshipList as List<string>;
-        const linkColorList = Doc.UserDoc().linkColorList as List<string>;
+        const linkColorList = Doc.UserDoc().link_ColorList as List<string>;
         const linkRelationshipSizes = Doc.UserDoc().link_relationshipSizes as List<number>;
         const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship);
         const linkDescription = Field.toString(link.link_description as any as Field);
@@ -262,11 +262,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         //thickness varies linearly from 3px to 12px for increasing link count
         const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
 
-        if (link.layout_linkDisplayArrow === undefined) {
-            link.layout_linkDisplayArrow = false;
+        if (link.link_displayArrow === undefined) {
+            link.link_displayArrow = false;
         }
 
-        return link.opacity === 0 || !a.width || !b.width || (!link.layout_linkDisplay && !aActive && !bActive) ? null : (
+        return link.opacity === 0 || !a.width || !b.width || (!link.link_displayLine && !aActive && !bActive) ? null : (
             <>
                 <defs>
                     <marker id={`${link[Id] + 'arrowhead'}`} markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
@@ -295,7 +295,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                     style={{ pointerEvents: 'visibleStroke', opacity: this._opacity, stroke, strokeWidth }}
                     onClick={this.onClickLine}
                     d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
-                    markerEnd={link.layout_linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
+                    markerEnd={link.link_displayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
                 />
                 {textX === undefined || !linkDescription ? null : (
                     <text filter={`url(#${link[Id] + 'background'})`} className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 811a77fa8..11151e74e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1021,7 +1021,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     zoom = (pointX: number, pointY: number, deltaY: number): void => {
-        if (this.Document._isGroup) return;
+        if (this.Document._isGroup || this.Document._freeform_noZoom) return;
         let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
         if (deltaScale < 0) deltaScale = -deltaScale;
         const [x, y] = this.getTransform().transformPoint(pointX, pointY);
@@ -1294,8 +1294,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
                 PanelWidth={childLayout[Width]}
                 PanelHeight={childLayout[Height]}
-                docFilters={this.childDocFilters}
-                docRangeFilters={this.childDocRangeFilters}
+                childFilters={this.childDocFilters}
+                childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
                 isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
                 isContentActive={this.props.childContentsActive ?? emptyFunction}
@@ -1317,7 +1317,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
                 pointerEvents={this.pointerEvents}
                 //rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
-                //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+                //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeViewFreezeChildDimensions)} // bcz: check this
             />
         );
     }
@@ -1692,7 +1692,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
         (e as any).handlePan = true;
 
-        if (!this.layoutDoc._noAutoscroll && !this.props.renderDepth && this._marqueeRef) {
+        if (!this.layoutDoc._freeform_noAutoPan && !this.props.renderDepth && this._marqueeRef) {
             const dragX = e.detail.clientX;
             const dragY = e.detail.clientY;
             const bounds = this._marqueeRef?.getBoundingClientRect();
@@ -2007,8 +2007,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                             onKey={this.onKeyDown}
                             onDoubleClick={this.onChildDoubleClickHandler}
                             onBrowseClick={this.onBrowseClickHandler}
-                            docFilters={this.childDocFilters}
-                            docRangeFilters={this.childDocRangeFilters}
+                            childFilters={this.childDocFilters}
+                            childFiltersByRanges={this.childDocRangeFilters}
                             searchFilterDocs={this.searchFilterDocs}
                             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
                             isContentActive={this.props.childContentsActive ?? emptyFunction}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d50c8d2c4..534275610 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -522,7 +522,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             followLinkToggle: true,
             _width: 200,
             _height: 200,
-            _layoutFitContentsToBox: true,
+            _layout_fitContentsToBox: true,
             _layout_showSidebar: true,
             title: 'overview',
         });
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index ca6b8705d..65578f214 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -24,7 +24,7 @@ import './CollectionLinearView.scss';
 /**
  * CollectionLinearView is the class for rendering the horizontal collection
  * of documents, it useful for horizontal menus. It can either be expandable
- * or not using the linearViewExpandable field.
+ * or not using the linearView_Expandable field.
  * It is used in the following locations:
  * - It is used in the popup menu on the bottom left (see docButtons() in MainView.tsx)
  * - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
@@ -46,7 +46,7 @@ export class CollectionLinearView extends CollectionSubView() {
 
     componentDidMount() {
         this._widthDisposer = reaction(
-            () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0),
+            () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0),
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
@@ -214,8 +214,8 @@ export class CollectionLinearView extends CollectionSubView() {
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
-                    docFilters={this.props.docFilters}
-                    docRangeFilters={this.props.docRangeFilters}
+                    childFilters={this.props.childFilters}
+                    childFiltersByRanges={this.props.childFiltersByRanges}
                     searchFilterDocs={this.props.searchFilterDocs}
                     hideResizeHandles={true}
                 />
@@ -226,7 +226,7 @@ export class CollectionLinearView extends CollectionSubView() {
     render() {
         const flexDir = StrCast(this.Document.flexDirection); // Specify direction of linear view content
         const flexGap = NumCast(this.Document.flexGap); // Specify the gap between linear view content
-        const isExpanded = BoolCast(this.layoutDoc.linearViewIsExpanded);
+        const isExpanded = BoolCast(this.layoutDoc.linearView_IsExpanded);
 
         const menuOpener = (
             <label
@@ -239,9 +239,9 @@ export class CollectionLinearView extends CollectionSubView() {
         );
 
         return (
-            <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: this.layoutDoc.linearViewIsExpanded ? undefined : 'transparent' }}>
+            <div className={`collectionLinearView-outer ${this.layoutDoc.linearView_SubMenu}`} style={{ backgroundColor: this.layoutDoc.linearView_IsExpanded ? undefined : 'transparent' }}>
                 <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension() }}>
-                    {!this.props.Document.linearViewExpandable ? null : (
+                    {!this.props.Document.linearView_Expandable ? null : (
                         <Tooltip title={<div className="dash-tooltip">{isExpanded ? 'Close' : 'Open'}</div>} placement="top">
                             {menuOpener}
                         </Tooltip>
@@ -259,11 +259,11 @@ export class CollectionLinearView extends CollectionSubView() {
                                 scriptContext: this.props.scriptContext,
                                 documentView: this.props.DocumentView?.(),
                             });
-                            this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked;
+                            this.layoutDoc.linearView_IsExpanded = this.addMenuToggle.current!.checked;
                         })}
                     />
 
-                    {!this.layoutDoc.linearViewIsExpanded ? null : (
+                    {!this.layoutDoc.linearView_IsExpanded ? null : (
                         <div
                             className="collectionLinearView-content"
                             style={{
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 78d3d1b6e..34fa0343d 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -259,8 +259,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 fitContentsToBox={this.props.fitContentsToBox}
                 focus={this.props.focus}
-                docFilters={this.childDocFilters}
-                docRangeFilters={this.childDocRangeFilters}
+                childFilters={this.childDocFilters}
+                childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
                 dontRegisterView={this.props.dontRegisterView}
                 addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4d61dc272..bab77ed48 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -258,8 +258,8 @@ export class CollectionMultirowView extends CollectionSubView() {
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 fitContentsToBox={this.props.fitContentsToBox}
                 focus={this.props.focus}
-                docFilters={this.childDocFilters}
-                docRangeFilters={this.childDocRangeFilters}
+                childFilters={this.childDocFilters}
+                childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
                 dontRegisterView={this.props.dontRegisterView}
                 addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 3a0c2c85c..52ebb7763 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -104,7 +104,7 @@
 
 .schema-header-row {
     cursor: grab;
-    justify-content: flex-end;
+    //justify-content: flex-end;
 
     .row-menu {
         display: flex;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 36abad87e..15424de98 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -32,6 +32,7 @@ export enum ColumnType {
     Date,
     Image,
     RTF,
+    Enumeration,
     Any,
 }
 
@@ -42,6 +43,7 @@ export const FInfotoColType: { [key: string]: ColumnType } = {
     date: ColumnType.Date,
     image: ColumnType.Image,
     rtf: ColumnType.RTF,
+    enumeration: ColumnType.Enumeration,
 };
 
 const defaultColumnKeys: string[] = ['title', 'type', 'author', 'author_date', 'text'];
@@ -54,6 +56,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     private _makeNewColumn: boolean = false;
     private _documentOptions: DocumentOptions = new DocumentOptions();
     private _tableContentRef: HTMLDivElement | null = null;
+    private _menuTarget = React.createRef<HTMLDivElement>();
 
     static _rowHeight: number = 50;
     static _rowSingleLineHeight: number = 32;
@@ -77,6 +80,11 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _filterSearchValue: string = '';
     @observable _selectedCell: [Doc, number] | undefined;
 
+    // target HTMLelement portal for showing a popup menu to edit cell values.
+    public get MenuTarget() {
+        return this._menuTarget.current;
+    }
+
     @computed get _selectedDocs() {
         return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
     }
@@ -336,9 +344,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     dragColumn = (e: PointerEvent, index: number) => {
         const dragData = new DragManager.ColumnDragData(index);
         const dragEles = [this._colEles[index]];
-        this.childDocs.forEach(doc => {
-            dragEles.push(this._rowEles.get(doc).children[1].children[index]);
-        });
+        this.childDocs.forEach(doc => dragEles.push(this._rowEles.get(doc).children[1].children[index]));
         DragManager.StartColumnDrag(dragEles, dragData, e.x, e.y);
 
         document.removeEventListener('pointermove', this.highlightDropColumn);
@@ -352,24 +358,28 @@ export class CollectionSchemaView extends CollectionSubView() {
         return true;
     };
 
-    @action
-    highlightDropColumn = (e: PointerEvent) => {
-        e.stopPropagation();
-        const mouseX = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+    findDropIndex = (mouseX: number) => {
         let index: number | undefined;
         this.displayColumnWidths.reduce((total, curr, i) => {
             if (total <= mouseX && total + curr >= mouseX) {
-                if (mouseX <= total + curr / 2) index = i;
+                if (mouseX <= total + curr) index = i;
                 else index = i + 1;
             }
             return total + curr;
         }, CollectionSchemaView._rowMenuWidth);
+        return index;
+    };
 
+    @action
+    highlightDropColumn = (e: PointerEvent) => {
+        e.stopPropagation();
+        const mouseX = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+        const index = this.findDropIndex(mouseX);
         this._colEles.forEach((colRef, i) => {
             let leftStyle = '';
             let rightStyle = '';
-            if (i + 1 === index) rightStyle = `solid 2px ${Colors.MEDIUM_BLUE}`;
-            if (i === index && i === 0) leftStyle = `solid 2px ${Colors.MEDIUM_BLUE}`;
+            if (i + 1 === index) rightStyle = `solid 12px ${Colors.MEDIUM_BLUE}`;
+            if (i === index && i === 0) leftStyle = `solid 12px ${Colors.MEDIUM_BLUE}`;
             colRef.style.borderLeft = leftStyle;
             colRef.style.borderRight = rightStyle;
             this.childDocs.forEach(doc => {
@@ -426,15 +436,8 @@ export class CollectionSchemaView extends CollectionSubView() {
         if (de.complete.columnDragData) {
             e.stopPropagation();
             const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0];
-            let index = de.complete.columnDragData.colIndex;
-            this.displayColumnWidths.reduce((total, curr, i) => {
-                if (total <= mouseX && total + curr >= mouseX) {
-                    if (mouseX <= total + curr / 2) index = i;
-                    else index = i + 1;
-                }
-                return total + curr;
-            }, CollectionSchemaView._rowMenuWidth);
-            this.moveColumn(de.complete.columnDragData.colIndex, index);
+            const index = this.findDropIndex(mouseX);
+            this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex);
 
             this._colEles.forEach((colRef, i) => {
                 colRef.style.borderLeft = '';
@@ -607,7 +610,7 @@ export class CollectionSchemaView extends CollectionSubView() {
         this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
     };
 
-    getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
+    getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(':')[0] == field);
 
     removeFieldFilters = (field: string) => {
         this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'));
@@ -760,7 +763,7 @@ export class CollectionSchemaView extends CollectionSubView() {
             }
         });
 
-        const filters = StrListCast(this.Document._docFilters);
+        const filters = StrListCast(this.Document._childFilters);
         return keyOptions.map(key => {
             let bool = false;
             if (filters !== undefined) {
@@ -832,6 +835,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     render() {
         return (
             <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}>
+                <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }}></div>
                 <div
                     className="schema-table"
                     onWheel={e => this.props.isContentActive() && e.stopPropagation()}
@@ -860,6 +864,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 openContextMenu={this.openContextMenu}
                                 dragColumn={this.dragColumn}
                                 setColRef={this.setColRef}
+                                isContentActive={this.props.isContentActive}
                             />
                         ))}
                     </div>
@@ -894,8 +899,8 @@ export class CollectionSchemaView extends CollectionSubView() {
                                 isContentActive={returnTrue}
                                 isDocumentActive={returnFalse}
                                 ScreenToLocalTransform={this.screenToLocal}
-                                docFilters={this.childDocFilters}
-                                docRangeFilters={this.childDocRangeFilters}
+                                childFilters={this.childDocFilters}
+                                childFiltersByRanges={this.childDocRangeFilters}
                                 searchFilterDocs={this.searchFilterDocs}
                                 styleProvider={DefaultStyleProvider}
                                 docViewPath={returnEmptyDoclist}
@@ -940,6 +945,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey)}
                                 Document={doc}
                                 DataDoc={dataDoc}
+                                yPadding={index}
                                 renderDepth={this.props.schema.props.renderDepth + 1}
                                 PanelWidth={this.tableWidthFunc}
                                 PanelHeight={this.props.rowHeight}
@@ -949,8 +955,8 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 enableDragWhenActive={true}
                                 onClickScriptDisable="always"
                                 focus={this.props.schema.focusDocument}
-                                docFilters={this.props.schema.childDocFilters}
-                                docRangeFilters={this.props.schema.childDocRangeFilters}
+                                childFilters={this.props.schema.childDocFilters}
+                                childFiltersByRanges={this.props.schema.childDocRangeFilters}
                                 searchFilterDocs={this.props.schema.searchFilterDocs}
                                 rootSelected={this.props.schema.rootSelected}
                                 ScreenToLocalTransform={this.childScreenToLocal(index)}
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 46c2f622b..65e47f441 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -12,6 +12,7 @@ export interface SchemaColumnHeaderProps {
     columnIndex: number;
     sortField: string;
     sortDesc: boolean;
+    isContentActive: (outsideReaction?: boolean | undefined) => boolean | undefined;
     setSort: (field: string | undefined, desc?: boolean) => void;
     removeColumn: (index: number) => void;
     rowHeight: () => number;
@@ -44,7 +45,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false);
+        this.props.isContentActive(true) && setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false);
     };
 
     render() {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 978b65053..4f3503751 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -7,7 +7,7 @@ import { Doc } from '../../../../fields/Doc';
 import { BoolCast } from '../../../../fields/Types';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoable } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
 import { OpenWhere } from '../../nodes/DocumentView';
@@ -15,6 +15,7 @@ import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { CollectionSchemaView } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 import { SchemaTableCell } from './SchemaTableCell';
+import { Transform } from '../../../util/Transform';
 
 @observer
 export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -143,6 +144,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             selectedCell={this.selectedCell}
                             setColumnValues={this.setColumnValues}
                             oneLine={BoolCast(this.schemaDoc?._singleLine)}
+                            menuTarget={this.schemaView.MenuTarget}
+                            transform={() => {
+                                const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index;
+                                const x = this.schemaView?.displayColumnWidths.reduce((p, c, i) => (i <= ind ? p + c : p), 0);
+                                const y = (this.props.yPadding ?? 0) * this.props.PanelHeight();
+                                return new Transform(x + CollectionSchemaView._rowMenuWidth, y, 1);
+                            }}
                         />
                     ))}
                 </div>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 876693474..97264508c 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,4 +1,5 @@
-import React = require('react');
+import * as React from 'react';
+import Select, { MenuPlacement } from 'react-select';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { extname } from 'path';
@@ -6,12 +7,11 @@ import DatePicker from 'react-datepicker';
 import { DateField } from '../../../../fields/DateField';
 import { Doc, DocListCast, Field } from '../../../../fields/Doc';
 import { RichTextField } from '../../../../fields/RichTextField';
-import { BoolCast, Cast, DateCast, DocCast, FieldValue } from '../../../../fields/Types';
+import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
 import { FInfo } from '../../../documents/Documents';
 import { DocFocusOrOpen } from '../../../util/DocumentManager';
-import { dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoable, undoBatch } from '../../../util/UndoManager';
 import { EditableView } from '../../EditableView';
@@ -21,7 +21,7 @@ import { FieldView, FieldViewProps } from '../../nodes/FieldView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { KeyValueBox } from '../../nodes/KeyValueBox';
 import { DefaultStyleProvider } from '../../StyleProvider';
-import { ColumnType, FInfotoColType } from './CollectionSchemaView';
+import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
 import './CollectionSchemaView.scss';
 
 export interface SchemaTableCellProps {
@@ -41,6 +41,9 @@ export interface SchemaTableCellProps {
     oneLine?: boolean; // whether all input should fit on one line vs allowing textare multiline inputs
     allowCRs?: boolean; // allow carriage returns in text input (othewrise CR ends the edit)
     finishEdit?: () => void; // notify container that edit is over (eg. to hide view in DashFieldView)
+    options?: string[];
+    menuTarget: HTMLDivElement | null;
+    transform: () => Transform;
 }
 
 @observer
@@ -64,8 +67,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
         const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue';
         const textDecoration = color !== 'black' && parenCount ? 'underline' : '';
         const fieldProps: FieldViewProps = {
-            docFilters: returnEmptyFilter,
-            docRangeFilters: returnEmptyFilter,
+            childFilters: returnEmptyFilter,
+            childFiltersByRanges: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
             docViewPath: returnEmptyDoclist,
@@ -129,15 +132,15 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
     }
 
     get getCellType() {
+        const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType;
         const cellValue = this.props.Document[this.props.fieldKey];
         if (cellValue instanceof ImageField) return ColumnType.Image;
         if (cellValue instanceof DateField) return ColumnType.Date;
         if (cellValue instanceof RichTextField) return ColumnType.RTF;
         if (typeof cellValue === 'number') return ColumnType.Any;
-        if (typeof cellValue === 'string') return ColumnType.Any;
+        if (typeof cellValue === 'string' && columnTypeStr !== 'enumeration') return ColumnType.Any;
         if (typeof cellValue === 'boolean') return ColumnType.Boolean;
 
-        const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType;
         if (columnTypeStr && columnTypeStr in FInfotoColType) {
             return FInfotoColType[columnTypeStr];
         }
@@ -152,6 +155,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             case ColumnType.Image: return <SchemaImageCell {...this.props} />;
             case ColumnType.Boolean: return <SchemaBoolCell {...this.props} />;
             case ColumnType.RTF: return <SchemaRTFCell {...this.props} />;
+            case ColumnType.Enumeration: return <SchemaEnumerationCell {...this.props} options={this.props.getFinfo(this.props.fieldKey)?.values?.map(val => val.toString())} />;
             case ColumnType.Date: // return <SchemaDateCell {...this.props} />;
             default:  return this.defaultCellContent;
         }
@@ -311,3 +315,38 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
         );
     }
 }
+@observer
+export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps> {
+    @computed get selected() {
+        const selected: [Doc, number] | undefined = this.props.selectedCell();
+        return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col;
+    }
+    render() {
+        const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props);
+        const options = this.props.options?.map(facet => ({ value: facet, label: facet }));
+        return (
+            <div className="schemaSelectionCell" style={{ display: 'flex', color, textDecoration, cursor, pointerEvents }}>
+                <div style={{ width: '100%' }}>
+                    <Select
+                        styles={{
+                            menuPortal: base => ({
+                                ...base,
+                                left: 0,
+                                top: 0,
+                                transform: `translate(${this.props.transform().TranslateX}px, ${this.props.transform().TranslateY}px)`,
+                                width: Number(base.width) * this.props.transform().Scale,
+                                zIndex: 9999,
+                            }),
+                        }}
+                        menuPortalTarget={this.props.menuTarget}
+                        menuPosition={'absolute'}
+                        placeholder={StrCast(this.props.Document[this.props.fieldKey], 'select...')}
+                        options={options}
+                        isMulti={false}
+                        onChange={val => KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
+                    />
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index b9d56541a..8324a97d9 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -25,7 +25,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
 
     getBackgroundColor = (): string => {
         const link_relationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
-        const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
+        const linkColorList = StrListCast(Doc.UserDoc().link_ColorList);
         let color = 'white';
         // if this link's relationship property is not default "link", set its color
         if (link_relationshipList) {
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 5f2d4a7b6..664a0fa4f 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -89,8 +89,8 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-                        docFilters={returnEmptyFilter}
-                        docRangeFilters={returnEmptyFilter}
+                        childFilters={returnEmptyFilter}
+                        childFiltersByRanges={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                     />
                 </div>
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 60480ac33..d84e34d52 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -57,7 +57,7 @@ export class TableBox extends React.Component<TableBoxProps> {
                                                             DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
                                                                 dragComplete: e => {
                                                                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
-                                                                        e.linkDocument.layout_linkDisplay = true;
+                                                                        e.linkDocument.link_displayLine = true;
                                                                         // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
                                                                         // e.annoDragData.linkSourceDoc.followLinkZoom = false;
                                                                     }
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index bd1952ecb..7723a088d 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -71,7 +71,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             e,
             this.onLinkButtonMoved,
             emptyFunction,
-            action((e, doubleTap) => doubleTap && DocumentView.showBackLinks(this.props.View.rootDoc)),
+            action((e, doubleTap) => {
+                doubleTap && DocumentView.showBackLinks(this.props.View.rootDoc);
+            }),
             undefined,
             undefined,
             action(() => (DocumentLinksButton.LinkEditorDocView = this.props.View))
@@ -191,7 +193,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
 
     @computed get filteredLinks() {
         const results = [] as Doc[];
-        const filters = this.props.View.props.docFilters();
+        const filters = this.props.View.props.childFilters();
         Array.from(new Set<Doc>(this.props.View.allLinks)).forEach(link => {
             if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.link_anchor_2 as Doc], filters, []).length || DocUtils.FilterDocs([link.link_anchor_1 as Doc], filters, []).length) {
                 results.push(link);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 687683e6e..461066b1b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -168,8 +168,8 @@ export interface DocumentViewSharedProps {
     setTitleFocus?: () => void;
     focus: DocFocusFunc;
     layout_fitWidth?: (doc: Doc) => boolean | undefined;
-    docFilters: () => string[];
-    docRangeFilters: () => string[];
+    childFilters: () => string[];
+    childFiltersByRanges: () => string[];
     searchFilterDocs: () => Doc[];
     layout_showTitle?: () => string;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
@@ -192,7 +192,7 @@ export interface DocumentViewSharedProps {
     hideCaptions?: boolean;
     ignoreAutoHeight?: boolean;
     forceAutoHeight?: boolean;
-    disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
+    disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
     onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
     enableDragWhenActive?: boolean;
     waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
@@ -664,7 +664,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-        if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
+        if (e && this.rootDoc._layout_hideContextMenu && Doc.noviceMode) {
             e.preventDefault();
             e.stopPropagation();
             //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
@@ -880,7 +880,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             ? true
             : false;
     };
-    docFilters = () => [...this.props.docFilters(), ...StrListCast(this.layoutDoc.docFilters)];
+    childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
     contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents);
     @computed get contents() {
         TraceMobx();
@@ -912,7 +912,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     docViewPath={this.props.viewPath}
                     thumbShown={this.thumbShown}
                     setContentView={this.setContentView}
-                    docFilters={this.docFilters}
+                    childFilters={this.childFilters}
                     NativeDimScaling={this.props.NativeDimScaling}
                     PanelHeight={this.panelHeight}
                     setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
@@ -924,7 +924,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     setTitleFocus={this.setTitleFocus}
                     layout_fieldKey={this.finalLayoutKey}
                 />
-                {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
+                {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints}
             </div>
         );
     }
@@ -960,12 +960,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         TraceMobx();
         return LinkManager.Instance.getAllRelatedLinks(this.rootDoc);
     }
-    hideLink = computedFn((link: Doc) => () => (link.layout_linkDisplay = false));
+    hideLink = computedFn((link: Doc) => () => (link.link_displayLine = false));
     @computed get allLinkEndpoints() {
         // the small blue dots that mark the endpoints of links
         TraceMobx();
         if (this.props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.layout_unrendered) return null;
-        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.layout_linkDisplay);
+        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.childFilters?.() ?? [], []).filter(d => d.link_displayLine);
         return filtered.map(link => (
             <div className="documentView-anchorCont" key={link[Id]}>
                 <DocumentView
@@ -1013,7 +1013,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 gumStream = stream;
                 recorder = new MediaRecorder(stream);
                 recorder.ondataavailable = async (e: any) => {
-                    const [{ result }] = await Networking.UploadFilesToServer({file: e.data});
+                    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);
@@ -1296,13 +1296,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     // shows a stacking view collection (by default, but the user can change) of all documents linked to the source
-    public static showBackLinks(linkSource: Doc) {
-        const docId = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish';
+    public static showBackLinks(linkAnchor: Doc) {
+        const docId = Doc.CurrentUserEmail + Doc.GetProto(linkAnchor)[Id] + '-pivotish';
         // prettier-ignore
-        DocServer.GetRefField(docId).then(docx => docx instanceof Doc &&
+        DocServer.GetRefField(docId).then(docx =>
             LightboxView.SetLightboxDoc(
-                docx || // reuse existing pivot view of documents, or else create a new collection
-                Docs.Create.StackingDocument([], { title: linkSource.title + '-pivot', _width: 500, _height: 500, linkSource, updateContentsScript: ScriptField.MakeScript('updateLinkCollection(self)') }, docId)
+                (docx as Doc) ?? // reuse existing pivot view of documents, or else create a new collection
+                Docs.Create.StackingDocument([], { title: linkAnchor.title + '-pivot', _width: 500, _height: 500, target: linkAnchor, updateContentsScript: ScriptField.MakeScript('updateLinkCollection(self, self.target)') }, docId)
             )
         );
     }
@@ -1490,8 +1490,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             // increase max auto height if document has been resized to be greater than current max
             () => NumCast(this.layoutDoc._height),
             action(height => {
-                const docMax = NumCast(this.layoutDoc.docMaxAutoHeight);
-                if (docMax && docMax < height) this.layoutDoc.docMaxAutoHeight = height;
+                const docMax = NumCast(this.layoutDoc.layout_maxAutoHeight);
+                if (docMax && docMax < height) this.layoutDoc.layout_maxAutoHeight = height;
             })
         );
         !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.AddView(this);
@@ -1577,16 +1577,16 @@ ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuff
     else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
 });
 
-ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
-    const linkSource = Cast(linkCollection.linkSource, Doc, null);
+ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSource: Doc) {
     const collectedLinks = DocListCast(Doc.GetProto(linkCollection).data);
     let wid = linkSource[Width]();
+    let embedding:Doc|undefined;
     const links = LinkManager.Links(linkSource);
     links.forEach(link => {
         const other = LinkManager.getOppositeAnchor(link, linkSource);
-        const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other;
+        const otherdoc = DocCast(other?.annotationOn ?? other);
         if (otherdoc && !collectedLinks?.some(d => Doc.AreProtosEqual(d, otherdoc))) {
-            const embedding = Doc.MakeEmbedding(otherdoc);
+            embedding = Doc.MakeEmbedding(otherdoc);
             embedding.x = wid;
             embedding.y = 0;
             embedding._lockedPosition = false;
@@ -1594,5 +1594,6 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
             Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', embedding);
         }
     });
+    embedding && DocServer.UPDATE_SERVER_CACHE();// if a new embedding was made, update the client's server cache so that it will not come back as a promise
     return links;
 });
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 4e348c67c..64f25cb22 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -57,8 +57,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
         const props: FieldViewProps = {
             Document: this.props.doc,
             DataDoc: this.props.doc,
-            docFilters: returnEmptyFilter,
-            docRangeFilters: returnEmptyFilter,
+            childFilters: returnEmptyFilter,
+            childFiltersByRanges: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
             docViewPath: returnEmptyDoclist,
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 73823041c..52f3575cb 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -87,8 +87,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
         const params = {
             rotateText: null,
             fontSizeFactor: 1,
-            minimumFontSize: NumCast(this.rootDoc._minFontSize, 8),
-            maximumFontSize: NumCast(this.rootDoc._maxFontSize, 1000),
+            minimumFontSize: NumCast(this.rootDoc._label_minFontSize, 8),
+            maximumFontSize: NumCast(this.rootDoc._label_maxFontSize, 1000),
             limitingDimension: 'both',
             horizontalAlign: 'center',
             verticalAlign: 'center',
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index f38ef634c..e86b881a8 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -53,7 +53,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             } 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.layout_autoMoveAnchors = false;
+                this.rootDoc.link_autoMoveAnchors = false;
             }
         }
         return false;
@@ -68,7 +68,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const y = NumCast(this.rootDoc[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 + '_useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
+        const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
         const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
         const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_1')
             ? 'link_anchor_1'
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 079e1b56d..86191de63 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -268,8 +268,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             addDocTab={returnFalse}
                             pinToPres={returnFalse}
                             dontRegisterView={true}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
+                            childFilters={returnEmptyFilter}
+                            childFiltersByRanges={returnEmptyFilter}
                             searchFilterDocs={returnEmptyDoclist}
                             renderDepth={0}
                             suppressSetHeight={true}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index f03954789..93e54ffb7 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -394,7 +394,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
 
     /**
@@ -550,8 +550,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     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.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+    transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()];
     infoWidth = () => this.props.PanelWidth() / 5;
     infoHeight = () => this.props.PanelHeight() / 5;
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
@@ -601,7 +601,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     bingMapReady = (map: any) => (this._bingMap = map.map);
     render() {
-        const renderAnnotations = (docFilters?: () => string[]) => null;
+        const renderAnnotations = (childFilters?: () => string[]) => null;
         return (
             <div className="mapBox" ref={this._ref}>
                 <div
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 18fcc951a..72f37b62c 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -391,7 +391,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
 
     /**
@@ -547,8 +547,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     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.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+    transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()];
     infoWidth = () => this.props.PanelWidth() / 5;
     infoHeight = () => this.props.PanelHeight() / 5;
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
@@ -558,7 +558,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return (window as any).Microsoft.Maps;
     }
     render() {
-        const renderAnnotations = (docFilters?: () => string[]) => null;
+        const renderAnnotations = (childFilters?: () => string[]) => null;
         return (
             <div className="MapBox2" ref={this._ref}>
                 <div
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 6b26f494b..577101445 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -59,7 +59,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                             fieldKey="data"
                             NativeWidth={returnZero}
                             NativeHeight={returnZero}
-                            docFilters={returnEmptyFilter}
+                            childFilters={returnEmptyFilter}
                             setHeight={emptyFunction}
                             isAnnotationOverlay={false}
                             select={emptyFunction}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6ee1b67f2..c210176b0 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -441,7 +441,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
     };
     @undoBatch
-    toggleSidebarType = () => (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform);
+    toggleSidebarType = () => (this.rootDoc.sidebar_collectionType = this.rootDoc.sidebar_collectionType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform);
     specificContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         const options = cm.findByDescription('Options...');
@@ -557,7 +557,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         };
         return (
             <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: '100%', right: 0, backgroundColor: `white` }}>
-                {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
+                {renderComponent(StrCast(this.layoutDoc.sidebar_collectionType))}
             </div>
         );
     }
diff --git a/src/client/views/nodes/QueryBox.scss b/src/client/views/nodes/QueryBox.scss
deleted file mode 100644
index b5f90aa1e..000000000
--- a/src/client/views/nodes/QueryBox.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.queryBox, .queryBox-dragging {
-    width: 100%;
-    height: 100%; 
-    position: absolute;
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
deleted file mode 100644
index 1b6056be6..000000000
--- a/src/client/views/nodes/QueryBox.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-// import React = require("react");
-// import { IReactionDisposer } from "mobx";
-// import { observer } from "mobx-react";
-// import { documentSchema } from "../../../new_fields/documentSchemas";
-// import { Id } from '../../../new_fields/FieldSymbols';
-// import { makeInterface, listSpec } from "../../../new_fields/Schema";
-// import { StrCast, Cast } from "../../../new_fields/Types";
-// import { ViewBoxAnnotatableComponent } from '../DocComponent';
-// import { SearchBox } from "../search/SearchBox";
-// import { FieldView, FieldViewProps } from './FieldView';
-// import "./QueryBox.scss";
-// import { List } from "../../../new_fields/List";
-// import { SnappingManager } from "../../util/SnappingManager";
-
-// type QueryDocument = makeInterface<[typeof documentSchema]>;
-// const QueryDocument = makeInterface(documentSchema);
-
-// @observer
-// export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryDocument>(QueryDocument) {
-//     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(QueryBox, fieldKey); }
-//     _docListChangedReaction: IReactionDisposer | undefined;
-//     componentDidMount() {
-//     }
-
-//     componentWillUnmount() {
-//         this._docListChangedReaction?.();
-//     }
-
-//     render() {
-//         const dragging = !SnappingManager.GetIsDragging() ? "" : "-dragging";
-//         return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} >
-            
-//             <SearchBox  Document={this.props.Document}  />
-//         </div >;
-//     }
-// }
-
-// //<SearchBox id={this.props.Document[Id]} sideBar={side} Document={this.props.Document} searchQuery={StrCast(this.dataDoc.searchQuery)} filterQuery={this.dataDoc.filterQuery} />
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 34dcde23b..1f52c2d92 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -913,7 +913,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     marqueeFitScaling = () => ((this.props.NativeDimScaling?.() || 1) * this.heightPercent) / 100;
     marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0];
 
-    timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
+    timelineDocFilter = () => [`_isTimelineLabel:true,${Utils.noRecursionHack}:x`];
 
     // renders video controls
     componentUI = (boundsLeft: number, boundsTop: number) => {
@@ -1082,7 +1082,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                             PanelHeight={this.panelHeight}
                             isAnyChildContentActive={returnFalse}
                             ScreenToLocalTransform={this.screenToLocalTransform}
-                            docFilters={this.timelineDocFilter}
+                            childFilters={this.timelineDocFilter}
                             select={emptyFunction}
                             focus={emptyFunction}
                             NativeDimScaling={returnOne}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 48f77469f..2ff0245d2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -900,7 +900,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
-        const renderAnnotations = (docFilters: () => string[]) => (
+        const renderAnnotations = (childFilters: () => string[]) => (
             <CollectionFreeFormView
                 {...this.props}
                 setContentView={this.setInnerContent}
@@ -919,7 +919,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 NativeDimScaling={returnOne}
                 focus={this.focus}
                 dropAction="embed"
-                docFilters={docFilters}
+                childFilters={childFilters}
                 select={emptyFunction}
                 isAnyChildContentActive={returnFalse}
                 bringToFront={emptyFunction}
@@ -995,8 +995,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
+    transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc)) return 'none';
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 601759dbb..5bba51ec8 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -95,7 +95,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
     // Determining UI Specs
     @computed get label() {
-        return StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
+        return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.title));
     }
     Icon = (color: string) => {
         const icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any;
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 1d41eb379..48f4c2afd 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -217,8 +217,8 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     whenChildContentsActiveChanged={returnFalse}
                     bringToFront={emptyFunction}
                     dontRegisterView={false}
-                    docFilters={this.props.tbox?.props.docFilters}
-                    docRangeFilters={this.props.tbox?.props.docRangeFilters}
+                    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/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 105fb9003..115777c18 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -132,7 +132,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return this._showSidebar ? '20%' : StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
     @computed get layout_autoHeight() {
         return (this.props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this.props.ignoreAutoHeight;
@@ -811,9 +811,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             icon: !this.Document._layout_noSidebar ? 'eye-slash' : 'eye',
         });
         uicontrols.push({
-            description: (this.Document._layout_altContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
-            event: () => (this.layoutDoc._layout_altContentUI = !this.layoutDoc._layout_altContentUI),
-            icon: !this.Document._layout_altContentUI ? 'eye-slash' : 'eye',
+            description: (this.Document._layout_enableAltContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
+            event: () => (this.layoutDoc._layout_enableAltContentUI = !this.layoutDoc._layout_enableAltContentUI),
+            icon: !this.Document._layout_enableAltContentUI ? 'eye-slash' : 'eye',
         });
         uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
         !Doc.noviceMode &&
@@ -1741,7 +1741,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
         const state = this._editorView!.state;
         const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
-        if (this.layoutDoc.sidebarViewType === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
+        if (this.layoutDoc.sidebar_collectionType === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
             try {
                 translateGoogleApi(curText, { from: 'en', to: 'es' }).then((result1: any) => {
                     setTimeout(
@@ -1837,7 +1837,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 return toNum(height) + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
             };
             const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + toHgt(child), margins);
-            const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+            const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.layout_maxAutoHeight, proseHeight), proseHeight);
             if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) {
                 // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
                 const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
@@ -1949,19 +1949,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         fitContentsToBox={this.fitContentsToBox}
                         noSidebar={true}
                         treeViewHideTitle={true}
-                        fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
+                        fieldKey={this.layoutDoc.sidebar_collectionType === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
                     />
                 </div>
             );
         };
         return (
             <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
-                {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
+                {renderComponent(StrCast(this.layoutDoc.sidebar_collectionType))}
             </div>
         );
     }
     cycleAlternateText = () => {
-        if (this.layoutDoc._layout_altContentUI) {
+        if (this.layoutDoc._layout_enableAltContentUI) {
             const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
             this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
         }
@@ -2016,7 +2016,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         const active = this.props.isContentActive() || this.props.isSelected();
         const selected = active;
         const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
-        const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
+        const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
         const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
@@ -2096,7 +2096,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
                     {this.noSidebar || this.Document._layout_noSidebar || this.props.dontSelectOnLoad || this.Document._createDocOnCR ? null : this.sidebarHandle}
                     {this.audioHandle}
-                    {this.layoutDoc._layout_altContentUI ? this.overlayAlternateIcon : null}
+                    {this.layoutDoc._layout_enableAltContentUI ? this.overlayAlternateIcon : null}
                 </div>
             </div>
         );
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 884811317..ac20126f1 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -488,8 +488,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
 
         if ((pinDataTypes?.filters && activeItem.presDocFilters !== undefined) || (!pinDataTypes && activeItem.presDocFilters !== undefined)) {
-            if (bestTarget.docFilters !== activeItem.presDocFilters) {
-                bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presDocFilters as ObjectField) || new List<string>([]);
+            if (bestTarget.childFilters !== activeItem.presDocFilters) {
+                bestTarget.childFilters = ObjectField.MakeCopy(activeItem.presDocFilters as ObjectField) || new List<string>([]);
                 changed = true;
             }
         }
@@ -651,7 +651,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     )
                 );
             if (pinProps.pinData.type_collection) pinDoc.presViewType = targetDoc._type_collection;
-            if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField);
+            if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField);
             if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField;
             if (pinProps.pinData.pannable) {
                 pinDoc.presPanX = NumCast(targetDoc._freeform_panX);
@@ -2078,10 +2078,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // prettier-ignore
         switch (layout) {
             case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
-            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
-            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
-            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
-            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y })
+            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y });
+            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y });
+            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y });
+            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _layout_fitContentsToBox: true, x, y })
         }
     };
 
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 154c879a0..34e069046 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -107,8 +107,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     ScreenToLocalTransform={Transform.Identity}
                     renderDepth={this.props.renderDepth + 1}
                     docViewPath={returnEmptyDoclist}
-                    docFilters={this.props.docFilters}
-                    docRangeFilters={this.props.docRangeFilters}
+                    childFilters={this.props.childFilters}
+                    childFiltersByRanges={this.props.childFiltersByRanges}
                     searchFilterDocs={this.props.searchFilterDocs}
                     rootSelected={returnTrue}
                     addDocument={returnFalse}
@@ -377,9 +377,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const recording = Docs.Create.WebCamDocument('', {
                 _width: 384,
                 _height: 216,
-                hideDocumentButtonBar: true,
+                layout_hideDocumentButtonBar: true,
                 layout_hideDecorationTitle: true,
-                hideOpenButton: true,
+                layout_hideOpenButton: true,
                 // hideDeleteButton: true,
                 cloneFieldFilter: new List<string>(['isSystem']),
             });
@@ -522,7 +522,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         style={{
                             display: 'infline-block',
                             backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
-                            //boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
+                            //layout_boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
                             border: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? presBoxColor + ' solid 2.5px' : undefined) : undefined,
                         }}>
                         <div
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bc0cad636..dd202418b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -91,7 +91,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @observable isAnnotating = false;
     // key where data is stored
     @computed get allAnnotations() {
-        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.props.docFilters(), this.props.docRangeFilters());
+        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.props.childFilters(), this.props.childFiltersByRanges());
     }
     @computed get inlineTextAnnotations() {
         return this.allAnnotations.filter(a => a.textInlineAnnotations);
@@ -513,8 +513,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
     overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._freeform_scale, 1));
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
+    transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc)) return 'none';
@@ -523,7 +523,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return this.props.styleProvider?.(doc, props, property);
     };
 
-    renderAnnotations = (docFilters: () => string[], mixBlendMode?: any, display?: string) => (
+    renderAnnotations = (childFilters: () => string[], mixBlendMode?: any, display?: string) => (
         <div
             className="pdfViewerDash-overlay"
             style={{
@@ -550,7 +550,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 isAnyChildContentActive={returnFalse}
                 isAnnotationOverlayScrollable={true}
                 dropAction="embed"
-                docFilters={docFilters}
+                childFilters={childFilters}
                 select={emptyFunction}
                 bringToFront={emptyFunction}
                 styleProvider={this.childStyleProvider}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 72eeedcff..92ae64567 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1415,18 +1415,18 @@ export namespace Doc {
     }
     export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: number[]) {
         if (!container) return;
-        const docRangeFilters = Cast(container._docRangeFilters, listSpec('string'), []);
-        for (let i = 0; i < docRangeFilters.length; i += 3) {
-            if (docRangeFilters[i] === key) {
-                docRangeFilters.splice(i, 3);
+        const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []);
+        for (let i = 0; i < childFiltersByRanges.length; i += 3) {
+            if (childFiltersByRanges[i] === key) {
+                childFiltersByRanges.splice(i, 3);
                 break;
             }
         }
         if (range !== undefined) {
-            docRangeFilters.push(key);
-            docRangeFilters.push(range[0].toString());
-            docRangeFilters.push(range[1].toString());
-            container._docRangeFilters = new List<string>(docRangeFilters);
+            childFiltersByRanges.push(key);
+            childFiltersByRanges.push(range[0].toString());
+            childFiltersByRanges.push(range[1].toString());
+            container._childFiltersByRanges = new List<string>(childFiltersByRanges);
         }
     }
 
@@ -1435,35 +1435,35 @@ export namespace Doc {
     // based on the modifiers :"check", "x", undefined
     export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
         if (!container) return;
-        const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'docFilters';
-        const docFilters = StrListCast(container[filterField]);
+        const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'childFilters';
+        const childFilters = StrListCast(container[filterField]);
         runInAction(() => {
-            for (let i = 0; i < docFilters.length; i++) {
-                const fields = docFilters[i].split(':'); // split key:value:modifier
+            for (let i = 0; i < childFilters.length; i++) {
+                const fields = childFilters[i].split(':'); // split key:value:modifier
                 if (fields[0] === key && (fields[1] === value || modifiers === 'match' || (fields[2] === 'match' && modifiers === 'remove'))) {
                     if (fields[2] === modifiers && modifiers && fields[1] === value) {
                         if (toggle) modifiers = 'remove';
                         else return;
                     }
-                    docFilters.splice(i, 1);
-                    container[filterField] = new List<string>(docFilters);
+                    childFilters.splice(i, 1);
+                    container[filterField] = new List<string>(childFilters);
                     break;
                 }
             }
-            if (!docFilters.length && modifiers === 'match' && value === undefined) {
+            if (!childFilters.length && modifiers === 'match' && value === undefined) {
                 container[filterField] = undefined;
             } else if (modifiers !== 'remove') {
-                !append && (docFilters.length = 0);
-                docFilters.push(key + ':' + value + ':' + modifiers);
-                container[filterField] = new List<string>(docFilters);
+                !append && (childFilters.length = 0);
+                childFilters.push(key + ':' + value + ':' + modifiers);
+                container[filterField] = new List<string>(childFilters);
             }
         });
     }
     export function readDocRangeFilter(doc: Doc, key: string) {
-        const docRangeFilters = Cast(doc._docRangeFilters, listSpec('string'), []);
-        for (let i = 0; i < docRangeFilters.length; i += 3) {
-            if (docRangeFilters[i] === key) {
-                return [Number(docRangeFilters[i + 1]), Number(docRangeFilters[i + 2])];
+        const childFiltersByRanges = Cast(doc._childFiltersByRanges, listSpec('string'), []);
+        for (let i = 0; i < childFiltersByRanges.length; i += 3) {
+            if (childFiltersByRanges[i] === key) {
+                return [Number(childFiltersByRanges[i + 1]), Number(childFiltersByRanges[i + 2])];
             }
         }
     }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 7a0a18929..757d507be 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -55,8 +55,8 @@ export const documentSchema = createSchema({
 
     // appearance properties on the data document
     backgroundColor: 'string', // background color of document
-    borderRounding: 'string', // border radius rounding of document
-    boxShadow: 'string', // the amount of shadow around the perimeter of a document
+    layout_borderRounding: 'string', // border radius rounding of document
+    layout_boxShadow: 'string', // the amount of shadow around the perimeter of a document
     color: 'string', // foreground color of document
     freeform_fitContentsToBox: 'boolean', // whether freeform view contents should be zoomed/panned to fill the area of the document view box
     fontSize: 'string',
@@ -86,13 +86,13 @@ export const documentSchema = createSchema({
     onDragStart: ScriptField, // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
     followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, )
     hideLinkButton: 'boolean', // whether the blue link counter button should be hidden
-    hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
-    layout_linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
+    layout_hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
+    link_displayLine: 'boolean', // whether a link connection should be shown between link anchor endpoints.
     isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations
     layers: listSpec('string'), // which layers the document is part of
     _lockedPosition: 'boolean', // whether the document can be moved (dragged)
     _lockedTransform: 'boolean', // whether a freeformview can pan/zoom
-    layout_linkDisplayArrow: 'boolean', // toggles directed arrows
+    link_displayArrow: 'boolean', // toggles directed arrows
 
     // drag drop properties
     _stayInCollection: 'boolean', // whether document can be dropped into a different collection
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 62f27c7d2..f365adf4b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -16,7 +16,7 @@ import { PrefetchProxy, ProxyField } from './Proxy';
 import { RefField } from './RefField';
 import { RichTextField } from './RichTextField';
 import { SchemaHeaderField } from './SchemaHeaderField';
-import { ComputedField } from './ScriptField';
+import { ComputedField, ScriptField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
 
 function _readOnlySetter(): never {
@@ -69,10 +69,12 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
 
     if (writeToDoc) {
         if (value === undefined) {
-            delete target[FieldKeys][prop];
+            target[FieldKeys] && delete target[FieldKeys][prop]; // Lists don't have a FieldKeys field
             delete target.__fieldTuples[prop];
         } else {
-            target[FieldKeys][prop] = true;
+            // bcz: uncomment to see if server is being updated
+            // console.log(prop + ' = ' + value + '(' + curValue + ')');
+            target[FieldKeys] && (target[FieldKeys][prop] = true); // Lists don't have a FieldKeys field
             target.__fieldTuples[prop] = value;
         }
 
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index c3423e2a5..7a1dde9fb 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -25,8 +25,8 @@ export class AudioUpload extends React.Component {
                 title: 'mobile audio',
                 _width: 300,
                 _height: 300,
-                _layoutFitContentsToBox: true,
-                boxShadow: '0 0',
+                _layout_fitContentsToBox: true,
+                layout_boxShadow: '0 0',
             }),
             Doc
         )
@@ -54,7 +54,7 @@ export class AudioUpload extends React.Component {
                             Doc
                         ) as Doc,
                     ],
-                    { title: 'mobile audio', _width: 300, _height: 300, _layoutFitContentsToBox: true, boxShadow: '0 0' }
+                    { title: 'mobile audio', _width: 300, _height: 300, _layout_fitContentsToBox: true, layout_boxShadow: '0 0' }
                 ),
                 Doc
             )
@@ -110,8 +110,8 @@ export class AudioUpload extends React.Component {
                         pinToPres={emptyFunction}
                         rootSelected={returnTrue}
                         removeDocument={undefined}
-                        docFilters={returnEmptyFilter}
-                        docRangeFilters={returnEmptyFilter}
+                        childFilters={returnEmptyFilter}
+                        childFiltersByRanges={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                         ScreenToLocalTransform={Transform.Identity}
                         PanelWidth={() => 600}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 471dc568b..584a7b432 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -402,8 +402,8 @@ export class MobileInterface extends React.Component {
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
+                    childFilters={returnEmptyFilter}
+                    childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
             </div>
-- 
cgit v1.2.3-70-g09d2


From 72dad52c4fea5a2fccabacbb6a49ca1494093954 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 22 Jun 2023 10:14:26 -0400
Subject: cleaned up dragging - abort presItem drags properly, fix naming for
 drag/dropAction, small fix undo of drag removeproperties, dragging in/out of
 headerBar,  fixed isContentActive to avoid SnappingManager.GetIsDragging. 
 fixed resizing videos.

---
 src/client/documents/Documents.ts                  | 49 +++++------
 src/client/util/CurrentUserUtils.ts                | 81 +++++++++---------
 src/client/util/DragManager.ts                     | 38 ++++-----
 src/client/util/DropConverter.ts                   |  8 +-
 src/client/util/UndoManager.ts                     | 19 ++---
 src/client/views/DashboardView.tsx                 |  6 +-
 src/client/views/DocComponent.tsx                  |  6 +-
 src/client/views/DocumentDecorations.tsx           |  8 +-
 src/client/views/GestureOverlay.tsx                | 95 +---------------------
 src/client/views/MainView.tsx                      | 12 ++-
 src/client/views/Palette.scss                      | 30 -------
 src/client/views/Palette.tsx                       | 69 ----------------
 src/client/views/PropertiesView.tsx                |  1 -
 src/client/views/StyleProvider.tsx                 |  1 +
 src/client/views/TemplateMenu.tsx                  |  5 +-
 .../views/collections/CollectionCarouselView.tsx   |  4 +-
 .../views/collections/CollectionNoteTakingView.tsx |  6 +-
 .../views/collections/CollectionPileView.tsx       | 11 ++-
 .../collections/CollectionStackedTimeline.scss     |  2 +-
 .../collections/CollectionStackedTimeline.tsx      | 10 ++-
 .../views/collections/CollectionStackingView.tsx   | 18 ++--
 src/client/views/collections/CollectionSubView.tsx | 10 +--
 .../views/collections/CollectionTimeView.tsx       |  2 -
 .../views/collections/CollectionTreeView.tsx       | 14 ++--
 src/client/views/collections/CollectionView.tsx    |  5 +-
 src/client/views/collections/TabDocView.tsx        |  1 -
 src/client/views/collections/TreeView.tsx          | 29 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 11 ++-
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 -
 .../collectionGrid/CollectionGridView.tsx          |  2 +-
 .../collectionLinear/CollectionLinearView.tsx      | 40 ++-------
 .../CollectionMulticolumnView.tsx                  | 18 ++--
 .../CollectionMultirowView.tsx                     | 19 +++--
 .../collectionSchema/CollectionSchemaView.tsx      |  3 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |  8 +-
 .../collectionSchema/SchemaTableCell.tsx           |  2 +-
 src/client/views/linking/LinkMenuItem.tsx          |  2 +-
 src/client/views/linking/LinkPopup.tsx             |  1 -
 src/client/views/nodes/ComparisonBox.scss          |  2 +-
 src/client/views/nodes/ComparisonBox.tsx           | 80 ++++++++++++------
 src/client/views/nodes/DocumentView.tsx            | 52 +++++++-----
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/ImageBox.tsx                |  4 +-
 src/client/views/nodes/KeyValueBox.tsx             |  8 +-
 src/client/views/nodes/KeyValuePair.tsx            |  1 -
 src/client/views/nodes/LinkAnchorBox.tsx           |  2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  5 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 -
 src/client/views/nodes/WebBox.tsx                  | 14 +++-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 21 ++---
 src/client/views/nodes/trails/PresBox.tsx          |  4 +-
 src/client/views/nodes/trails/PresElementBox.tsx   | 13 +--
 src/client/views/pdf/PDFViewer.tsx                 | 18 ++--
 src/fields/Doc.ts                                  |  2 +-
 src/fields/documentSchemas.ts                      | 10 +--
 src/fields/util.ts                                 |  4 +-
 57 files changed, 356 insertions(+), 537 deletions(-)
 delete mode 100644 src/client/views/Palette.scss
 delete mode 100644 src/client/views/Palette.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ffde9fe1b..a61ef1da3 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -25,7 +25,7 @@ import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox';
 import { FollowLinkScript } from '../util/LinkFollower';
 import { LinkManager } from '../util/LinkManager';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
+import { undoable, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionView } from '../views/collections/CollectionView';
@@ -184,7 +184,8 @@ export class DocumentOptions {
     opacity?: NUMt = new NumInfo('document opacity');
     viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters');
     dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc');
-    dontUndo?: BOOLt = new BoolInfo('whether button clicks should be undoable (true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)');
+    _undoIgnoreFields?: List<string>; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
+    undoIgnoreFields?: List<string>; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
     _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
     _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
     _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
@@ -355,20 +356,19 @@ export class DocumentOptions {
     waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     onPointerDown?: ScriptField;
     onPointerUp?: ScriptField;
-    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
     openFactoryAsDelegate?: BOOLt = new BoolInfo('create a delegate of the factory');
     _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)');
-    enableDragWhenActive?: BOOLt = new BoolInfo('allow dragging even if document contentts are active (e.g., tree, groups)');
-    _stayInCollection?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere');
+    _dragOnlyWithinContainer?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere');
     _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.');
-    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true);
-    childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
-    targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
+    childDragAction?: DROPt = new DAInfo('what should happen to the child documents when they are dragged fromt he collection');
+    dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
+    dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
     _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
-    _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
+    _dropPropertiesToRemove?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
-    dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
+    dragAction?: DROPt = new DAInfo('how to drag document when it is active (e.g., tree, groups)');
+    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
@@ -536,7 +536,7 @@ export namespace Docs {
                         link_description: '',
                         layout_showCaption: 'link_description',
                         backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
-                        _removeDropProperties: new List(['onClick']),
+                        _dropPropertiesToRemove: new List(['onClick']),
                     },
                 },
             ],
@@ -545,7 +545,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'embed', title: 'Global Link Database' },
+                    options: { title: 'Global Link Database' },
                 },
             ],
             [
@@ -553,7 +553,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'embed', title: 'Global Script Database' },
+                    options: { title: 'Global Script Database' },
                 },
             ],
             [
@@ -615,7 +615,7 @@ export namespace Docs {
                 DocumentType.FONTICON,
                 {
                     layout: { view: FontIconBox, dataField: 'icon' },
-                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, layout_hideLinkButton: true, _width: 40, _height: 40 },
+                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
             [
@@ -659,7 +659,7 @@ export namespace Docs {
                 {
                     data: '',
                     layout: { view: ComparisonBox, dataField: defaultDataKey },
-                    options: { backgroundColor: 'gray', targetDropAction: 'embed' },
+                    options: { backgroundColor: 'gray', dropAction: 'move', waitForDoubleClickToClick: 'always' },
                 },
             ],
             [
@@ -667,7 +667,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'embed', title: 'Global Group Database' },
+                    options: { childDragAction: 'embed', title: 'Global Group Database' },
                 },
             ],
             [
@@ -1066,12 +1066,7 @@ export namespace Docs {
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(
-                Prototypes.get(DocumentType.COL),
-                new List(documents),
-                { enableDragWhenActive: true, _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
-                id
-            );
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile }, id);
         }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1523,7 +1518,7 @@ export namespace DocUtils {
         !simpleMenu &&
             ContextMenu.Instance.addItem({
                 description: 'Quick Notes',
-                subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({
+                subitems: DocListCast((Doc.UserDoc()['template_notes'] as Doc).data).map((note, i) => ({
                     description: ':' + StrCast(note.title),
                     event: undoable((args: { x: number; y: number }) => {
                         const textDoc = Docs.Create.TextDocument('', {
@@ -1583,10 +1578,10 @@ export namespace DocUtils {
     }
     export function findTemplate(templateName: string, type: string, signature: string) {
         let docLayoutTemplate: Opt<Doc>;
-        const iconViews = DocListCast(Cast(Doc.UserDoc()['template-icons'], Doc, null)?.data);
-        const templBtns = DocListCast(Cast(Doc.UserDoc()['template-buttons'], Doc, null)?.data);
-        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data);
-        const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template-clickFuncs'], Doc, null)?.data);
+        const iconViews = DocListCast(Cast(Doc.UserDoc()['template_icons'], Doc, null)?.data);
+        const templBtns = DocListCast(Cast(Doc.UserDoc()['template_buttons'], Doc, null)?.data);
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
+        const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
         const allTemplates = iconViews
             .concat(templBtns)
             .concat(noteTypes)
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 592f6d836..11a8dcaf6 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -97,7 +97,7 @@ export class CurrentUserUtils {
 
         const reqdOpts:DocumentOptions = {
             title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true,
-            _stayInCollection: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true,
+            _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true,
             _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
@@ -123,7 +123,7 @@ export class CurrentUserUtils {
     }
 
     /// Initializes templates for editing click funcs of a document 
-    static setupClickEditorTemplates(doc: Doc, field = "template-clickFuncs") {
+    static setupClickEditorTemplates(doc: Doc, field = "template_clickFuncs") {
         const tempClicks = DocCast(doc[field]);
         const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
@@ -144,7 +144,7 @@ export class CurrentUserUtils {
     }
 
     /// Initializes templates that can be applied to notes
-    static setupNoteTemplates(doc: Doc, field="template-notes") {
+    static setupNoteTemplates(doc: Doc, field="template_notes") {
         const tempNotes = DocCast(doc[field]);
         const reqdTempOpts:DocumentOptions[] = [
             { noteType: "Note", backgroundColor: "yellow", icon: "sticky-note"},
@@ -174,7 +174,7 @@ export class CurrentUserUtils {
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
-    static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
+    static setupDefaultIconTemplates(doc: Doc, field="template_icons") {
         const reqdOpts = { title: "icon templates", _height: 75, isSystem: true };
         const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
@@ -279,11 +279,11 @@ export class CurrentUserUtils {
             {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, treeViewHideUnrendered: true}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, targetDropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
                                                                                                 treeViewHasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
-                                                                                                allowOverlayDrop: true, treeViewType: TreeViewType.outline, 
+                                                                                                dropAction:'move', treeViewType: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
@@ -322,17 +322,16 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
             const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, 
-                _width: 35, _height: 35, _layout_hideContextMenu: true, _stayInCollection: true, 
+                _width: 35, _height: 35, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true, 
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
-                _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
         });
 
         const reqdOpts:DocumentOptions = {
-            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
+            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
             _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-             childDropAction: 'embed' 
+             childDragAction: 'embed' 
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -357,7 +356,7 @@ export class CurrentUserUtils {
 
     /// the empty panel that is filled with whichever left menu button's panel has been selected
     static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
-        DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.isSystem = true; return doc;})(new Doc()), {isSystem:true});
+        DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
@@ -367,15 +366,14 @@ export class CurrentUserUtils {
         const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
-                title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true,
-                _width: 60, _height: 60,  _stayInCollection: true, _layout_hideContextMenu: true,
-                _removeDropProperties: new List<string>(["_stayInCollection"]),
+                title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
+                _width: 60, _height: 60,  _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
             };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
         });
 
         const reqdStackOpts:DocumentOptions ={
-            title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            title: "menuItemPanel", childDragAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
             _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
@@ -458,7 +456,7 @@ export class CurrentUserUtils {
     /// Search option on the left side button panel
     static setupSearcher(doc: Doc, field:string) {
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDropAction: "embed", 
+                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: "embed", 
                 _lockedPosition: true, _type_collection: CollectionViewType.Schema  });
     }
 
@@ -469,7 +467,7 @@ export class CurrentUserUtils {
         const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
             title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
-            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, 
+            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, 
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
     }
@@ -481,7 +479,7 @@ export class CurrentUserUtils {
         const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
         const newDashboard = `createNewDashboard()`;
 
-        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true,
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
              title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true };
         const reqdBtnScript = {onClick: newDashboard,}
         const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
@@ -495,8 +493,8 @@ export class CurrentUserUtils {
         const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
-            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
-            layout_headerButton: newDashboardButton, childDropAction: "embed",
+            dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
+            layout_headerButton: newDashboardButton, childDragAction: "embed",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
@@ -520,20 +518,20 @@ export class CurrentUserUtils {
     /// initializes the left sidebar File system pane
     static setupFilesystem(doc: Doc, field:string) {
         var myFilesystem = DocCast(doc[field]);
-        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, isSystem: true, isFolder: true });
+        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", undoIgnoreFields:new List<string>(['treeViewSortCriterion']), _dragOnlyWithinContainer: true, isSystem: true, isFolder: true });
         
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
-            _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _width: 30, _height: 30,
+            _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
             title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: newFolder};
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
-            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true,
+            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: "proto", isSystem: true,
             isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
-            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed",
+            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
             childContextMenuLabels: new List<string>(["Create new folder"]),
             childContextMenuIcons: new List<string>(["plus"]),
             layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
@@ -549,8 +547,8 @@ export class CurrentUserUtils {
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
-            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true,
-            treeViewTruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
+            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "embed", isSystem: true,
+            treeViewTruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
             layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
@@ -558,7 +556,7 @@ export class CurrentUserUtils {
         const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
 
         const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
-        const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, 
+        const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, 
             title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", isSystem: true,
             toolTip: "Empty recently closed",};
         DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
@@ -575,7 +573,7 @@ export class CurrentUserUtils {
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
-            layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true,
+            layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", ignoreClick: true, isSystem: true,
             treeViewHideTitle: true, treeViewTruncateTitleWidth: 350
         };
         if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
@@ -590,7 +588,7 @@ export class CurrentUserUtils {
 
     static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
         btnType: ButtonType.ToolButton, _forceActive: true, _layout_hideContextMenu: true, 
-        _removeDropProperties: new List<string>([ "_layout_hideContextMenu", "stayInCollection"]), 
+        _dropPropertiesToRemove: new List<string>([ "_layout_hideContextMenu"]), 
         _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts,
     })
 
@@ -608,9 +606,9 @@ export class CurrentUserUtils {
             { scripts: { },                       opts: { title: "linker",            layout: "<LinkingUI>",          toolTip: "link started"}},
             { scripts: { },                       opts: { title: "currently playing", layout: "<CurrentlyPlayingUI>", toolTip: "currently playing media"}},
         ];
-        const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
+        const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
-            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDropAction: 'embed',
+            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
             childDontRegisterViews: true, linearView_IsExpanded: true, linearView_Expandable: true, ignoreClick: true
         };
         reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
@@ -716,12 +714,11 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
             backgroundColor: params.backgroundColor ??"transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
-            color: Colors.WHITE, isSystem: true, //dontUndo: true, 
+            color: Colors.WHITE, isSystem: true,
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
             toolType: params.toolType, expertMode: params.expertMode,
-            _stayInCollection: true, _layout_hideContextMenu: true, _lockedPosition: true,
-            _removeDropProperties: new List<string>([ "_stayInCollection"]),
+            _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _lockedPosition: true,
         };
         const reqdFuncs:{[key:string]:any} = {
             ...params.funcs,
@@ -732,14 +729,14 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsExpanded"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true,  linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
             if (!params.subMenu) {
                 return this.setupContextMenuButton(params, menuBtnDoc);
             } else {
-                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, dontUndo: true,
+                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsExpanded"]),
                     childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
                     linearView_SubMenu: true, linearView_Expandable: true, };
                 const items = params.subMenu?.map(sub =>
@@ -786,13 +783,13 @@ export class CurrentUserUtils {
         const sharedDocOpts:DocumentOptions = {
             title: "My Shared Docs",
             userColor: "rgb(202, 202, 202)",
-            isFolder:true,
+            isFolder:true, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
             childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
             childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
             childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
-            childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
+            childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
             // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
             _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
             layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
@@ -805,14 +802,14 @@ export class CurrentUserUtils {
     static setupImportSidebar(doc: Doc, field:string) {   
         const reqdOpts:DocumentOptions = {
             title: "My Imports", _forceActive: true, ignoreClick: true, _layout_showTitle: "title", 
-            _stayInCollection: true, _layout_hideContextMenu: true, childLimitHeight: 0,
-            childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
+            _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0,
+            childDragAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
             dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go."
         };
         const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
     
         const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
-            _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
+            _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
             buttonText: "Import", icon: "upload", isSystem: true };
         DocUtils.AssignDocField(myImports, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
         return myImports;
@@ -864,7 +861,7 @@ export class CurrentUserUtils {
         this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true }); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
         
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 87833d322..85101fcab 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,42 +15,36 @@ import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
 
-export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties
+export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
 
 /**
  * Initialize drag
  * @param _reference: The HTMLElement that is being dragged
  * @param docFunc: The Dash document being moved
- * @param moveFunc: The function called when the document is moved
- * @param dropAction: What to do with the document when it is dropped
- * @param dragStarted: Method to call when the drag is started
  */
-export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc | undefined> | undefined, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, dragStarted?: () => void) {
-    const onRowMove = async (e: PointerEvent) => {
+export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | undefined) {
+    const onRowMove = (e: PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
 
         document.removeEventListener('pointermove', onRowMove);
         document.removeEventListener('pointerup', onRowUp);
-        const doc = await docFunc();
+        const doc = docFunc();
         if (doc) {
             const dragData = new DragManager.DocumentDragData([doc]);
-            dragData.dropAction = dropAction;
-            dragData.moveDocument = moveFunc;
             DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
-            dragStarted?.();
         }
     };
     const onRowUp = (): void => {
         document.removeEventListener('pointermove', onRowMove);
         document.removeEventListener('pointerup', onRowUp);
     };
-    const onItemDown = async (e: React.PointerEvent) => {
+    const onItemDown = (e: React.PointerEvent) => {
         if (e.button === 0) {
             e.stopPropagation();
             if (e.shiftKey) {
                 e.persist();
-                const dragDoc = await docFunc();
+                const dragDoc = docFunc();
                 dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]);
             } else {
                 document.addEventListener('pointermove', onRowMove);
@@ -132,7 +126,7 @@ export namespace DragManager {
         userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
         defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
         dropAction: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
-        removeDropProperties?: string[];
+        dropPropertiesToRemove?: string[];
         moveDocument?: MoveFunction;
         removeDocument?: RemoveFunction;
         isDocDecorationMove?: boolean; // Flags that Document decorations are used to drag document which allows suppression of onDragStart scripts
@@ -189,7 +183,7 @@ export namespace DragManager {
         const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
         const preDropHandler = (e: Event) => {
             const de = (e as CustomEvent<DropEvent>).detail;
-            (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.targetDropAction) as dropActionType);
+            (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.dropAction) as dropActionType);
         };
         element.addEventListener('dashOnDrop', handler);
         doc && element.addEventListener('dashPreDrop', preDropHandler);
@@ -230,11 +224,13 @@ export namespace DragManager {
                     )
                 ).filter(d => d);
                 !['same', 'proto'].includes(docDragData.dropAction as any) &&
-                    docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
-                        const dragProps = StrListCast(dragData.draggedDocuments[i].removeDropProperties);
-                        const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
-                        remProps.map(prop => (drop[prop] = undefined));
-                    });
+                    docDragData.droppedDocuments
+                        // .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
+                        .forEach((drop: Doc, i: number) => {
+                            const dragProps = StrListCast(dragData.draggedDocuments[i].dropPropertiesToRemove);
+                            const remProps = (dragData?.dropPropertiesToRemove || []).concat(Array.from(dragProps));
+                            [...remProps, 'dropPropertiesToRemove'].map(prop => (drop[prop] = undefined));
+                        });
             }
             return e;
         };
@@ -586,6 +582,7 @@ export namespace DragManager {
 
     async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
         const dropArgs = {
+            cancelable: true, // allows preventDefault() to be called to cancel the drop
             bubbles: true,
             detail: {
                 ...pos,
@@ -598,8 +595,9 @@ export namespace DragManager {
             },
         };
         target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
+        UndoManager.StartTempBatch(); // run drag/drop in temp batch in case drop is not allowed (so we can undo any intermediate changes)
         await finishDrag?.(complete);
-        target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs));
+        UndoManager.EndTempBatch(target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs))); // event return val is true unless the event preventDefault() is called
         options?.dragComplete?.(complete);
         endDrag?.();
     }
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index f46ea393a..47997cc5c 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -57,11 +57,11 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
         let dbox = doc;
         // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
         if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) {
-            if (data.removeDropProperties || dbox.removeDropProperties) {
+            if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) {
                 //dbox = Doc.MakeEmbedding(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
                 dbox = Doc.MakeEmbedding(dbox);
-                const dragProps = Cast(dbox.removeDropProperties, listSpec('string'), []);
-                const remProps = (data.removeDropProperties || []).concat(Array.from(dragProps));
+                const dragProps = Cast(dbox.dropPropertiesToRemove, listSpec('string'), []);
+                const remProps = (data.dropPropertiesToRemove || []).concat(Array.from(dragProps));
                 remProps.map(prop => (dbox[prop] = undefined));
             }
         } else if (!doc.onDragStart && !doc.isButtonBar) {
@@ -81,7 +81,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
                 icon: layoutDoc.isTemplateDoc ? 'font' : 'bolt',
             });
             dbox.dragFactory = layoutDoc;
-            dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
+            dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
             dbox.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory)');
         } else if (doc.isButtonBar) {
             dbox.ignoreClick = true;
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 6fef9d660..2379cb2ab 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -91,7 +91,7 @@ export namespace UndoManager {
     let currentBatch: UndoBatch | undefined;
     export let batchCounter = observable.box(0);
     let undoing = false;
-    let tempEvents: UndoEvent[] | undefined = undefined;
+    export let tempEvents: UndoEvent[] | undefined = undefined;
 
     export function AddEvent(event: UndoEvent, value?: any): void {
         if (currentBatch && batchCounter.get() && !undoing) {
@@ -187,15 +187,11 @@ export namespace UndoManager {
         return false;
     });
 
-    export function RunInTempBatch<T>(fn: () => T) {
+    export function StartTempBatch() {
         tempEvents = [];
-        try {
-            const success = runInAction(fn);
-            if (!success) UndoManager.UndoTempBatch();
-            return success;
-        } finally {
-            tempEvents = undefined;
-        }
+    }
+    export function EndTempBatch<T>(success: boolean) {
+        UndoManager.UndoTempBatch(success);
     }
     //TODO Make this return the return value
     export function RunInBatch<T>(fn: () => T, batchName: string) {
@@ -206,10 +202,11 @@ export namespace UndoManager {
             batch.end();
         }
     }
-    export const UndoTempBatch = action(() => {
-        if (tempEvents) {
+    export const UndoTempBatch = action((success: any) => {
+        if (tempEvents && !success) {
             undoing = true;
             for (let i = tempEvents.length - 1; i >= 0; i--) {
+                currentBatch?.includes(tempEvents[i]) && currentBatch.splice(currentBatch.indexOf(tempEvents[i]));
                 tempEvents[i].undo();
             }
             undoing = false;
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 50cf2226e..6aae302ac 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -389,7 +389,7 @@ export class DashboardView extends React.Component {
             _forceActive: true,
             _width: 30,
             _height: 30,
-            _stayInCollection: true,
+            _dragOnlyWithinContainer: true,
             _layout_hideContextMenu: true,
             title: 'New trail',
             toolTip: 'Create new trail',
@@ -412,7 +412,7 @@ export class DashboardView extends React.Component {
             _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
-            childDropAction: 'embed',
+            childDragAction: 'embed',
             treeViewTruncateTitleWidth: 150,
             ignoreClick: true,
             layout_headerButton: myTrailsBtn,
@@ -421,7 +421,7 @@ export class DashboardView extends React.Component {
             _lockedPosition: true,
             layout_boxShadow: '0 0',
             childDontRegisterViews: true,
-            targetDropAction: 'same',
+            dropAction: 'same',
             isSystem: true,
             layout_explainer: 'All of the trails that you have created will appear here.',
         };
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index db24229dc..0709d6cb9 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -172,8 +172,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 return true;
             }
             const first = doc instanceof Doc ? doc : doc[0];
-            if (!first?._stayInCollection && addDocument !== returnFalse) {
-                return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey));
+            if (!first?._dragOnlyWithinContainer && addDocument !== returnFalse) {
+                return this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey);
             }
             return false;
         };
@@ -213,7 +213,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                         .map(doc => {
                             // only make a pushpin if we have acl's to edit the document
                             //DocUtils.LeavePushpin(doc);
-                            doc._stayInCollection = undefined;
+                            doc._dragOnlyWithinContainer = undefined;
                             doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 528b82e3e..41ce08d3c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -603,7 +603,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     let actualdH = Math.max(docheight + dH * scale, 20);
                     let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
                     let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
-                    const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false;
+                    const preserveNativeDim = !doc._nativeHeightUnfrozen && !doc._nativeDimModifiable;
                     const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
                     if (fixedAspect) {
                         if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) {
@@ -636,7 +636,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 }
                             }
                             if (!modifyNativeDim) {
-                                actualdH = (nheight / nwidth) * docwidth; //, actualdH);
+                                actualdH = (nheight / nwidth) * NumCast(doc._width); //, actualdH);
                             }
                             doc._height = actualdH;
                         }
@@ -756,7 +756,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             hideDecorations ||
             seldocview.props.hideOpenButton ||
             seldocview.rootDoc.layout_hideOpenButton ||
-            SelectionManager.Views().some(docView => docView.rootDoc._stayInCollection || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) ||
+            SelectionManager.Views().some(docView => docView.rootDoc._dragOnlyWithinContainer || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) ||
             this._isRounding ||
             this._isRotating;
         const hideDeleteButton =
@@ -767,7 +767,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
-                return (docView.rootDoc.stayInCollection && !docView.rootDoc._isTimelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin;
             });
 
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 0951bff22..54ad519de 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -4,8 +4,7 @@ import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../fields/Doc';
 import { InkData, InkTool } from '../../fields/InkField';
-import { ScriptField } from '../../fields/ScriptField';
-import { Cast, FieldValue, NumCast } from '../../fields/Types';
+import { NumCast } from '../../fields/Types';
 import MobileInkOverlay from '../../mobile/MobileInkOverlay';
 import { GestureUtils } from '../../pen-gestures/GestureUtils';
 import { MobileInkOverlayContent } from '../../server/Message';
@@ -35,7 +34,6 @@ import { InkTranscription } from './InkTranscription';
 import { checkInksToGroup } from './nodes/button/FontIconBox';
 import { DocumentView } from './nodes/DocumentView';
 import { RadialMenu } from './nodes/RadialMenu';
-import HorizontalPalette from './Palette';
 import { Touchable } from './Touchable';
 import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu';
 
@@ -79,7 +77,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
     private _overlayRef = React.createRef<HTMLDivElement>();
     private _d1: Doc | undefined;
     private _inkToTextDoc: Doc | undefined;
-    private _thumbDoc: Doc | undefined;
     private thumbIdentifier?: number;
     private pointerIdentifier?: number;
     private _hands: Map<number, React.Touch[]> = new Map<number, React.Touch[]>();
@@ -93,78 +90,12 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
         GestureOverlay.Instances.push(this);
     }
 
-    static setupThumbButtons(doc: Doc) {
-        const docProtoData: { title: string; icon: string; drag?: string; toolType?: string; ignoreClick?: boolean; pointerDown?: string; pointerUp?: string; clipboard?: Doc; backgroundColor?: string; dragFactory?: Doc }[] = [
-            { title: 'use pen', icon: 'pen-nib', pointerUp: 'resetPen()', pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: 'blue' },
-            { title: 'use highlighter', icon: 'highlighter', pointerUp: 'resetPen()', pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: 'yellow' },
-            {
-                title: 'notepad',
-                icon: 'clipboard',
-                pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()',
-                pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)',
-                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, isSystem: true }),
-                backgroundColor: 'orange',
-            },
-            { title: 'interpret text', icon: 'font', toolType: 'inktotext', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'orange' },
-            { title: 'ignore gestures', icon: 'signature', toolType: 'ignoregesture', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'green' },
-        ];
-        return docProtoData.map(data =>
-            Docs.Create.FontIconDocument({
-                _nativeWidth: 10,
-                _nativeHeight: 10,
-                _width: 10,
-                _height: 10,
-                title: data.title,
-                icon: data.icon,
-                toolType: data.toolType,
-                _dropAction: data.pointerDown ? 'copy' : undefined,
-                ignoreClick: data.ignoreClick,
-                onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
-                clipboard: data.clipboard,
-                onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
-                onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
-                backgroundColor: data.backgroundColor,
-                dragFactory: data.dragFactory,
-                isSystem: true,
-            })
-        );
-    }
-
-    static setupThumbDoc(userDoc: Doc) {
-        if (!userDoc.thumbDoc) {
-            const thumbDoc = Docs.Create.LinearDocument(GestureOverlay.setupThumbButtons(userDoc), {
-                _width: 100,
-                _height: 50,
-                ignoreClick: true,
-                _lockedPosition: true,
-                title: 'buttons',
-                _layout_autoHeight: true,
-                _yMargin: 5,
-                linearView_IsExpanded: true,
-                backgroundColor: 'white',
-                isSystem: true,
-            });
-            thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
-                _width: 300,
-                _height: 25,
-                _layout_autoHeight: true,
-                linearView_IsExpanded: true,
-                flexDirection: 'column',
-                isSystem: true,
-            });
-            userDoc.thumbDoc = thumbDoc;
-        }
-        return Cast(userDoc.thumbDoc, Doc);
-    }
-
     componentWillUnmount() {
         GestureOverlay.Instances.splice(GestureOverlay.Instances.indexOf(this), 1);
         GestureOverlay.Instance = GestureOverlay.Instances.lastElement();
     }
     componentDidMount = () => {
         GestureOverlay.Instance = this;
-        this._thumbDoc = FieldValue(Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc));
-        this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
     };
 
     // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group
@@ -395,24 +326,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
 
         this.thumbIdentifier = thumb?.identifier;
         this._hands.set(thumb.identifier, fingers);
-        const others = fingers.filter(f => f !== thumb);
-        const minX = Math.min(...others.map(f => f.clientX));
-        const minY = Math.min(...others.map(f => f.clientY));
-
-        // load up the palette collection around the thumb
-        const thumbDoc = await Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc);
-        if (thumbDoc) {
-            runInAction(() => {
-                RadialMenu.Instance._display = false;
-                this._inkToTextDoc = FieldValue(Cast(thumbDoc.inkToTextDoc, Doc));
-                this._thumbDoc = thumbDoc;
-                this._thumbX = thumb.clientX;
-                this._thumbY = thumb.clientY;
-                this._menuX = thumb.clientX + 50;
-                this._menuY = thumb.clientY;
-                this._palette = <HorizontalPalette key="palette" x={minX} y={minY} thumb={[thumb.clientX, thumb.clientY]} thumbDoc={thumbDoc} />;
-            });
-        }
 
         this.removeMoveListeners();
         document.removeEventListener('touchmove', this.handleHandMove);
@@ -462,11 +375,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                         if (Math.abs(pt.clientY - this._thumbY) > 10 * window.devicePixelRatio) {
                             this._selectedIndex = Math.min(Math.max(-1, -Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1), this._possibilities.length - 1);
                         }
-                    } else if (this._thumbDoc) {
-                        if (Math.abs(pt.clientX - this._thumbX) > 15 * window.devicePixelRatio) {
-                            this._thumbDoc.selectedIndex = Math.max(-1, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX));
-                            this._thumbX = pt.clientX;
-                        }
                     }
                 }
             }
@@ -485,7 +393,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
             if (this.thumbIdentifier) this._hands.delete(this.thumbIdentifier);
             this._palette = undefined;
             this.thumbIdentifier = undefined;
-            this._thumbDoc = undefined;
 
             // this chunk of code is for handling the ink to text toolglass
             let scriptWorked = false;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1a9d15c1a..dbe8fb608 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -30,6 +30,7 @@ import { ColorScheme, SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
+import { UndoManager } from '../util/UndoManager';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
@@ -571,7 +572,7 @@ export class MainView extends React.Component {
 
     @action
     createNewFolder = async () => {
-        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
         Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
     };
 
@@ -582,6 +583,8 @@ export class MainView extends React.Component {
     waitForDoubleClick = () => (this._exploreMode ? 'never' : undefined);
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
     mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1);
+    addHeaderDoc = (doc: Doc | Doc[], annotationKey?: string) => (doc instanceof Doc ? [doc] : doc).reduce((done, doc) => Doc.AddDocToList(this.headerBarDoc, 'data', doc), true);
+    removeHeaderDoc = (doc: Doc | Doc[], annotationKey?: string) => (doc instanceof Doc ? [doc] : doc).reduce((done, doc) => Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true);
     @computed get headerBarDocView() {
         return (
             <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
@@ -589,18 +592,19 @@ export class MainView extends React.Component {
                     key="headerBarDoc"
                     Document={this.headerBarDoc}
                     DataDoc={undefined}
-                    addDocument={undefined}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
                     pinToPres={emptyFunction}
                     docViewPath={returnEmptyDoclist}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
-                    removeDocument={returnFalse}
+                    addDocument={this.addHeaderDoc}
+                    removeDocument={this.removeHeaderDoc}
                     fitContentsToBox={returnTrue}
                     isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
                     isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                     ScreenToLocalTransform={this.headerBarScreenXf}
                     childHideResizeHandles={returnTrue}
+                    childDragAction="move"
                     dontRegisterView={true}
                     hideResizeHandles={true}
                     PanelWidth={this.headerBarDocWidth}
@@ -865,7 +869,7 @@ export class MainView extends React.Component {
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true);
     moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
     addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true);
 
diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss
deleted file mode 100644
index 0ec879288..000000000
--- a/src/client/views/Palette.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-.palette-container {
-    .palette-thumb {
-        touch-action: pan-x;
-        position: absolute;
-        height: 70px;
-        overflow: hidden;
-
-        .palette-thumbContent {
-            transition: transform .3s;
-            width: max-content;
-            overflow: hidden;
-
-            .collectionView {
-                overflow: visible;
-
-                .collectionLinearView-outer {
-                    overflow: visible;
-                }
-            }
-        }
-
-        .palette-cover {
-            width: 50px;
-            height: 50px;
-            position: absolute;
-            bottom: 0;
-            border: 1px solid black;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
deleted file mode 100644
index 749eb08a2..000000000
--- a/src/client/views/Palette.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { IReactionDisposer, observable, reaction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../fields/Doc';
-import { NumCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from '../../Utils';
-import { Transform } from '../util/Transform';
-import { DocumentView } from './nodes/DocumentView';
-import './Palette.scss';
-
-export interface PaletteProps {
-    x: number;
-    y: number;
-    thumb: number[];
-    thumbDoc: Doc;
-}
-
-@observer
-export default class Palette extends React.Component<PaletteProps> {
-    private _selectedDisposer?: IReactionDisposer;
-    @observable private _selectedIndex: number = 0;
-
-    componentDidMount = () => {
-        this._selectedDisposer = reaction(
-            () => NumCast(this.props.thumbDoc.selectedIndex),
-            i => (this._selectedIndex = i),
-            { fireImmediately: true }
-        );
-    };
-
-    componentWillUnmount = () => {
-        this._selectedDisposer?.();
-    };
-
-    render() {
-        return (
-            <div className="palette-container" style={{ transform: `translate(${this.props.x}px, ${this.props.y}px)` }}>
-                <div className="palette-thumb" style={{ transform: `translate(${this.props.thumb[0] - this.props.x}px, ${this.props.thumb[1] - this.props.y}px)` }}>
-                    <div className="palette-thumbContent" style={{ transform: `translate(-${this._selectedIndex * 50 + 10}px, 0px)` }}>
-                        <DocumentView
-                            Document={this.props.thumbDoc}
-                            DataDoc={undefined}
-                            addDocument={undefined}
-                            addDocTab={returnFalse}
-                            rootSelected={returnTrue}
-                            pinToPres={emptyFunction}
-                            removeDocument={undefined}
-                            ScreenToLocalTransform={Transform.Identity}
-                            PanelWidth={() => window.screen.width}
-                            PanelHeight={() => window.screen.height}
-                            renderDepth={0}
-                            isDocumentActive={returnTrue}
-                            isContentActive={emptyFunction}
-                            focus={emptyFunction}
-                            docViewPath={returnEmptyDoclist}
-                            styleProvider={returnEmptyString}
-                            whenChildContentsActiveChanged={emptyFunction}
-                            bringToFront={emptyFunction}
-                            childFilters={returnEmptyFilter}
-                            childFiltersByRanges={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                        />
-                        <div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 1f2e21dd5..09aac053a 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -287,7 +287,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         pinToPres={emptyFunction}
                         bringToFront={returnFalse}
                         dontRegisterView={true}
-                        dropAction={undefined}
                     />
                 </div>
             );
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 2cca2c5a9..330ccc583 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -265,6 +265,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         }
         case StyleProp.PointerEvents:
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString;
+            if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc
             if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.();
             if (MainView.Instance._exploreMode || doc?.layout_unrendered) return isInk ? 'visiblePainted' : 'all';
             if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents);
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 62f47ba71..5c2ab3f70 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -97,8 +97,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         TraceMobx();
         const firstDoc = this.props.docViews[0].props.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);
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
+        const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={'default'} name={'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
@@ -122,7 +122,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                     rootSelected={returnFalse}
                     onCheckedClick={this.scriptField}
                     onChildClick={this.scriptField}
-                    dropAction={undefined}
                     isAnyChildContentActive={returnFalse}
                     isContentActive={returnTrue}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 0eb61a0b2..ea02bcd4c 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Opt } from '../../../fields/Doc';
 import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { returnFalse, returnZero, StopEvent } from '../../../Utils';
+import { emptyFunction, returnFalse, returnZero, StopEvent } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -60,6 +60,8 @@ export class CollectionCarouselView extends CollectionSubView() {
                         NativeHeight={returnZero}
                         onDoubleClick={this.onContentDoubleClick}
                         onClick={this.onContentClick}
+                        isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.props.isContentActive}
+                        isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction}
                         hideCaptions={show_captions ? true : false}
                         renderDepth={this.props.renderDepth + 1}
                         LayoutTemplate={this.props.childLayoutTemplate}
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 675f23970..9a3ab8074 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -10,7 +10,7 @@ import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, returnFalse, returnZero, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -250,7 +250,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
                 rootSelected={this.rootSelected}
                 layout_showTitle={this.props.childlayout_showTitle}
-                dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={noteTakingDocTransform}
@@ -619,7 +619,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     }
 
     @computed get backgroundEvents() {
-        return SnappingManager.GetIsDragging();
+        return this.props.isContentActive() === false ? 'none' : undefined;
     }
 
     observer: any;
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 57d9bbb49..15570b195 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -52,18 +52,21 @@ export class CollectionPileView extends CollectionSubView() {
     @computed get toggleIcon() {
         return ScriptField.MakeScript('documentView.iconify()', { documentView: 'any' });
     }
+    @computed get contentEvents() {
+        const isStarburst = this.layoutEngine() === computeStarburstLayout.name;
+        return this.props.isContentActive() && isStarburst ? undefined : 'none';
+    }
 
     // returns the contents of the pileup in a CollectionFreeFormView
     @computed get contents() {
-        const isStarburst = this.layoutEngine() === computeStarburstLayout.name;
         return (
-            <div className="collectionPileView-innards" style={{ pointerEvents: isStarburst || SnappingManager.GetIsDragging() ? undefined : 'none' }}>
+            <div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}>
                 <CollectionFreeFormView
-                    {...this.props}
+                    {...this.props} //
                     childContentsActive={returnFalse}
                     layoutEngine={this.layoutEngine}
                     addDocument={this.addPileDoc}
-                    childCanEmbedOnDrag={true}
+                    childDragAction="move"
                     childClickScript={this.toggleIcon}
                     moveDocument={this.removePileDoc}
                 />
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index a55b70e22..a19d8e696 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -104,7 +104,7 @@
 
         .collectionStackedTimeline-left-resizer,
         .collectionStackedTimeline-resizer {
-            background: $medium-gray;
+            background: $dark-gray;
             position: absolute;
             top: 0;
             height: 100%;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 9d5cb257a..e835fa659 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -119,8 +119,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         // onClick play scripts
         CollectionStackedTimeline.RangeScript =
             CollectionStackedTimeline.RangeScript ||
-            ScriptField.MakeFunction(`setTimeout(() => scriptContext.clickAnchor(this, clientX))`, {
-                // setTimeout is a hack to run script in its own properly named undo group (instead of being part of the generic onClick)
+            ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
                 self: Doc.name,
                 scriptContext: 'any',
                 clientX: 'number',
@@ -406,7 +405,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
                 _label_minFontSize: 12,
                 _label_maxFontSize: 24,
-                _stayInCollection: true,
+                _dragOnlyWithinContainer: true,
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
                 layout_hideLinkButton: true,
                 onClick: FollowLinkScript(),
@@ -528,6 +527,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         );
     }
 
+    @computed get timelineEvents() {
+        return this.props.isContentActive() ? 'all' : this.props.isContentActive() === false ? 'none' : undefined;
+    }
     render() {
         const overlaps: {
             anchorStartTime: number;
@@ -540,7 +542,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
         return this.clipDuration === 0 ? null : (
-            <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
+            <div ref={this.createDashEventsTarget} style={{ pointerEvents: this.timelineEvents }}>
                 <div
                     className="collectionStackedTimeline-timelineContainer"
                     style={{ width: this.props.PanelWidth(), cursor: SnappingManager.GetIsDragging() ? 'grab' : '' }}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 3c0d8cbc3..8eed4f716 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -17,7 +17,7 @@ import { CollectionViewType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
@@ -295,7 +295,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             return this.addDocument?.(newDoc);
         }
     };
-    isContentActive = () => (this.props.isSelected() || this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined);
+    isContentActive = () => (this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined);
 
     @observable _renderCount = 5;
     isChildContentActive = () =>
@@ -341,7 +341,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
                 rootSelected={this.rootSelected}
                 layout_showTitle={this.props.childlayout_showTitle}
-                dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={stackedDocTransform}
@@ -402,11 +402,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     // This following three functions must be from the view Mehek showed
     columnDividerDown = (e: React.PointerEvent) => {
         runInAction(() => (this._cursor = 'grabbing'));
+        const batch = UndoManager.StartBatch('stacking width');
         setupMoveUpEvents(
             this,
             e,
             this.onDividerMove,
-            action(() => (this._cursor = 'grab')),
+            action(() => {
+                this._cursor = 'grab';
+                batch.end();
+            }),
             emptyFunction
         );
     };
@@ -700,7 +704,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     }
 
     @computed get backgroundEvents() {
-        return SnappingManager.GetIsDragging();
+        return this.props.isContentActive() === false ? 'none' : undefined;
     }
     observer: any;
     render() {
@@ -710,7 +714,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             SetValue: this.addGroup,
             contents: '+ ADD A GROUP',
         };
-        const buttonMenu = this.rootDoc.buttonMenu;
+        const buttonMenu = this.rootDoc.layout_headerButton;
         const noviceExplainer = this.rootDoc.layout_explainer;
         return (
             <>
@@ -727,7 +731,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         style={{
                             overflowY: this.isContentActive() ? 'auto' : 'hidden',
                             background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
-                            pointerEvents: (this.props.pointerEvents?.() as any) ?? (this.backgroundEvents ? 'all' : undefined),
+                            pointerEvents: (this.props.pointerEvents?.() as any) ?? this.backgroundEvents,
                         }}
                         onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
                         onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d1b7f6ff6..539dde7e0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -187,7 +187,7 @@ export function CollectionSubView<X>(moreProps?: X) {
 
         protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
             if (de.complete.docDragData) {
-                // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
+                // if dropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
                 // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys)
                 if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
                     de.complete.docDragData.dropAction = targetAction;
@@ -212,16 +212,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
-                        const canAdd = this.props.Document._type_collection === CollectionViewType.Pile || de.embedKey || this.props.Document.allowOverlayDrop || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
-                        added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
+                        const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
+                        added = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
                     } else {
-                        ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
+                        ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                         added = addedDocs.length ? this.addDocument(addedDocs) : true;
                     }
                     added && e.stopPropagation();
                     return added;
                 } else {
-                    ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
+                    ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                     added = this.addDocument(docDragData.droppedDocuments);
                 }
                 !added && alert('You cannot perform this move');
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 60e6815e5..192d48c64 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -34,8 +34,6 @@ export class CollectionTimeView extends CollectionSubView() {
 
     async componentDidMount() {
         this.props.setContentView?.(this);
-        //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), "");
-        ///const childText = "const embedding = getEmbedding(self); switchView(embedding, detailView); embedding.dropAction='embed'; useRightSplit(embedding, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ed1e0c067..7598bb753 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,7 +3,6 @@ import { observer } from 'mobx-react';
 import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
 import { DocData, Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
-import { InkTool } from '../../../fields/InkField';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -90,8 +89,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
-    isContentActive = (outsideReaction?: boolean) =>
-        Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction) ? true : false;
+    isContentActive = (outsideReaction?: boolean) => (this._isAnyChildContentActive ? true : this.props.isContentActive() ? true : false);
 
     componentWillUnmount() {
         this._isDisposing = true;
@@ -261,7 +259,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _renderCount = 1;
     @computed get treeViewElements() {
         TraceMobx();
-        const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
+        const dragAction = StrCast(this.doc.childDragAction) as dropActionType;
         const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false;
         if (this._renderCount < this.treeChildren.length) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20))));
@@ -276,7 +274,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             addDoc,
             this.remove,
             moveDoc,
-            dropAction,
+            dragAction,
             this.props.addDocTab,
             this.props.styleProvider,
             this.screenToLocalTransform,
@@ -383,7 +381,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _headerHeight = 0;
     @computed get content() {
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
-        const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined);
+        const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined);
         const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
         return (
             <div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}>
@@ -440,12 +438,12 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                         setContentView={emptyFunction}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
-                        pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone}
+                        pointerEvents={this.props.isContentActive() && SnappingManager.GetIsDragging() ? returnAll : returnNone}
                         isAnnotationOverlay={true}
                         isAnnotationOverlayScrollable={true}
                         childDocumentsActive={this.props.isDocumentActive}
                         fieldKey={this.props.fieldKey + '_annotations'}
-                        dropAction={'move'}
+                        dropAction="move"
                         select={emptyFunction}
                         addDocument={this.addAnnotationDocument}
                         removeDocument={this.remAnnotationDocument}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7913d3188..c33519afb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -9,6 +9,7 @@ import { TraceMobx } from '../../../fields/util';
 import { returnEmptyString } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { CollectionViewType } from '../../documents/DocumentTypes';
+import { dropActionType } from '../../util/DragManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ContextMenu } from '../ContextMenu';
@@ -55,7 +56,7 @@ interface CollectionViewProps_ extends FieldViewProps {
     childHideDecorationTitle?: () => boolean;
     childHideResizeHandles?: () => boolean;
     childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
-    childCanEmbedOnDrag?: boolean;
+    childDragAction?: dropActionType;
     childXPadding?: number;
     childYPadding?: number;
     childLayoutString?: string;
@@ -220,7 +221,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
     childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
-    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() || this.isAnyChildContentActive();
+    isContentActive = (outsideReaction?: boolean) => (this.isAnyChildContentActive() ? true : this.props.isContentActive());
 
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 3b99339af..97d4d989b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -583,7 +583,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     isContentActive={emptyFunction}
                     isAnyChildContentActive={returnFalse}
                     select={emptyFunction}
-                    dropAction={undefined}
                     isSelected={returnFalse}
                     dontRegisterView={true}
                     fieldKey={Doc.LayoutFieldKey(this.props.document)}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6bc30c451..91ae2b3cc 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -21,7 +21,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
 import { EditableView } from '../EditableView';
 import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
 import { DocumentView, DocumentViewInternal, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
@@ -45,7 +45,7 @@ export interface TreeViewProps {
     dataDoc?: Doc;
     treeViewParent: Doc;
     renderDepth: number;
-    dropAction: dropActionType;
+    dragAction: dropActionType;
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
     panelWidth: () => number;
     panelHeight: () => number;
@@ -276,7 +276,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc);
-        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging() && this.props.isContentActive()) {
             this._header.current!.className = 'treeView-header';
             document.removeEventListener('pointermove', this.onDragMove, true);
             document.removeEventListener('pointerup', this.onDragUp, true);
@@ -341,7 +341,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
 
     makeFolder = () => {
-        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
         TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
         return this.props.addDocument(folder);
     };
@@ -391,7 +391,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd;
         if (canAdd) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
-            const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false));
+            const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false);
             return res;
         }
@@ -460,7 +460,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     addDoc,
                     remDoc,
                     moveDoc,
-                    this.props.dropAction,
+                    this.props.dragAction,
                     this.props.addDocTab,
                     this.titleStyleProvider,
                     this.props.ScreenToLocalTransform,
@@ -585,12 +585,12 @@ export class TreeView extends React.Component<TreeViewProps> {
                             downY = e.clientY;
                             e.stopPropagation();
                         }}
-                        onClick={e => {
+                        onClick={undoable(e => {
                             if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
                                 !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
                                 e.stopPropagation();
                             }
-                        }}>
+                        }, 'sort order')}>
                         {!docs
                             ? null
                             : TreeView.GetChildElements(
@@ -604,7 +604,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                   addDoc,
                                   remDoc,
                                   moveDoc,
-                                  StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType,
+                                  StrCast(this.doc.childDragAction, this.props.dragAction) as dropActionType,
                                   this.props.addDocTab,
                                   this.titleStyleProvider,
                                   this.props.ScreenToLocalTransform,
@@ -904,7 +904,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
-                enableDragWhenActive={true}
                 onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
                 docViewPath={this.props.treeView.props.docViewPath}
                 treeViewDoc={this.props.treeView.props.Document}
@@ -914,7 +913,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 pinToPres={emptyFunction}
                 onClick={this.onChildClick}
                 onDoubleClick={this.onChildDoubleClick}
-                dropAction={this.props.dropAction}
+                dragAction={this.props.dragAction}
                 moveDocument={this.move}
                 removeDocument={this.props.removeDoc}
                 ScreenToLocalTransform={this.getTransform}
@@ -950,7 +949,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined,
                         textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined,
                         outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
-                        pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+                        pointerEvents: !this.props.isContentActive() ? 'none' : undefined,
                     }}>
                     {view}
                 </div>
@@ -1132,7 +1131,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
         remove: undefined | ((doc: Doc | Doc[]) => boolean),
         move: DragManager.MoveFunction,
-        dropAction: dropActionType,
+        dragAction: dropActionType,
         addDocTab: (doc: Doc, where: OpenWhere) => boolean,
         styleProvider: undefined | StyleProviderFunc,
         screenToLocalXf: () => Transform,
@@ -1218,7 +1217,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         panelHeight={rowHeight}
                         dontRegisterView={dontRegisterView}
                         moveDocument={move}
-                        dropAction={dropAction}
+                        dragAction={dragAction}
                         addDocTab={addDocTab}
                         ScreenToLocalTransform={screenToLocalXf}
                         isContentActive={isContentActive}
@@ -1239,6 +1238,6 @@ export class TreeView extends React.Component<TreeViewProps> {
 
 ScriptingGlobals.add(function TreeView_addNewFolder() {
     TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
-    const opts = { title: 'Untitled folder', _stayInCollection: true, isFolder: true };
+    const opts = { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true };
     return Doc.AddDocToList(Doc.MyFilesystem, 'data', Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
 });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 36472dd2e..d7e073c5f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1240,7 +1240,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         };
     };
 
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isContentActive = () => this.props.isContentActive();
 
     @undoBatch
     @action
@@ -1267,7 +1267,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
         const pointerEvents = DocumentDecorations.Instance.Interacting
             ? 'none'
-            : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
+            : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
         return pointerEvents;
     };
     getChildDocView(entry: PoolData) {
@@ -1298,7 +1298,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
                 isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
-                isContentActive={this.props.childContentsActive ?? emptyFunction}
+                isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction}
                 focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
                 addDocTab={this.addDocTab}
                 addDocument={this.props.addDocument}
@@ -1308,10 +1308,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                 docViewPath={this.props.docViewPath}
                 styleProvider={this.getClusterColor}
-                canEmbedOnDrag={this.props.childCanEmbedOnDrag}
+                dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
                 dataProvider={this.childDataProvider}
                 sizeProvider={this.childSizeProvider}
-                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
                 bringToFront={this.bringToFront}
                 layout_showTitle={this.props.childlayout_showTitle}
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
@@ -1987,7 +1986,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 onDragOver={e => e.preventDefault()}
                 onContextMenu={this.onContextMenu}
                 style={{
-                    pointerEvents: SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement()) ? 'all' : (this.props.pointerEvents?.() as any),
+                    pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : (this.props.pointerEvents?.() as any),
                     textAlign: this.isAnnotationOverlay ? 'initial' : undefined,
                     transform: `scale(${this.nativeDimScaling || 1})`,
                     width: `${100 / (this.nativeDimScaling || 1)}%`,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 77cb7f88c..898452ccb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -386,7 +386,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
         newCollection.forceActive = makeGroup;
-        newCollection.enableDragWhenActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
         newCollection.layout_fitWidth = true;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index e8ae88ae5..cd8b7a0cc 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -368,7 +368,7 @@ export class CollectionGridView extends CollectionSubView() {
             <div
                 className="collectionGridView-contents"
                 ref={this.createDashEventsTarget}
-                style={{ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined }}
+                style={{ pointerEvents: !this.props.isContentActive() ? 'none' : undefined }}
                 onContextMenu={this.onContextMenu}
                 onPointerDown={this.onPointerDown}
                 onDrop={this.onExternalDrop}>
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 65578f214..8f90e4444 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Opt } from '../../../../fields/Doc';
@@ -50,34 +50,10 @@ export class CollectionLinearView extends CollectionSubView() {
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
-
-        this._selectedDisposer = reaction(
-            () => NumCast(this.layoutDoc.selectedIndex),
-            i =>
-                runInAction(() => {
-                    this._selectedIndex = i;
-                    let selected: any = undefined;
-                    this.childLayoutPairs.map(async (pair, ind) => {
-                        const isSelected = this._selectedIndex === ind;
-                        if (isSelected) {
-                            selected = pair;
-                        } else {
-                            ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
-                        }
-                    });
-                    if (selected && selected.layout) {
-                        ScriptCast(DocCast(selected.layout.proto)?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
-                    }
-                }),
-            { fireImmediately: true }
-        );
     }
     protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
-        //used for stacking and masonry view
-        this._dropDisposer && this._dropDisposer();
-        if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
-        }
+        this._dropDisposer?.();
+        if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
     };
 
     dimension = () => NumCast(this.rootDoc._height);
@@ -89,12 +65,8 @@ export class CollectionLinearView extends CollectionSubView() {
 
     @action
     exitLongLinks = () => {
-        if (DocumentLinksButton.StartLink) {
-            if (DocumentLinksButton.StartLink.Document) {
-                action((e: React.PointerEvent<HTMLDivElement>) => {
-                    Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
-                });
-            }
+        if (DocumentLinksButton.StartLink?.Document) {
+            action((e: React.PointerEvent<HTMLDivElement>) => Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc));
         }
         DocumentLinksButton.StartLink = undefined;
         DocumentLinksButton.StartLinkView = undefined;
@@ -201,7 +173,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     moveDocument={this.props.moveDocument}
                     addDocTab={this.props.addDocTab}
                     pinToPres={emptyFunction}
-                    dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                    dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
                     rootSelected={this.props.isSelected}
                     removeDocument={this.props.removeDocument}
                     ScreenToLocalTransform={docXf}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 34fa0343d..32b42223b 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -5,7 +5,6 @@ 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 { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -194,11 +193,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(
-                action((d: Doc) => {
-                    curInd = this.childDocs.indexOf(d);
-                })
-            );
+            de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d)));
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.x < de.x && brect.x + brect.width > de.x) {
@@ -234,7 +229,14 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
-    isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false);
+    isChildContentActive = () => {
+        const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
+        return this.props.isContentActive?.() === false || childDocsActive === false
+            ? false //
+            : this.props.isDocumentActive?.() && childDocsActive
+            ? true
+            : undefined;
+    };
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return (
             <DocumentView
@@ -248,7 +250,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 PanelWidth={width}
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
-                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                dragAction={(this.props.Document.childDragAction ?? this.props.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 suppressSetHeight={true}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index bab77ed48..fa3a9f352 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -5,7 +5,6 @@ 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 { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -194,11 +193,7 @@ export class CollectionMultirowView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(
-                action((d: Doc) => {
-                    curInd = this.childDocs.indexOf(d);
-                })
-            );
+            de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d)));
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.y < de.y && brect.y + brect.height > de.y) {
@@ -234,7 +229,14 @@ export class CollectionMultirowView extends CollectionSubView() {
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
-    isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false);
+    isChildContentActive = () => {
+        const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
+        return this.props.isContentActive?.() === false || childDocsActive === false
+            ? false //
+            : this.props.isDocumentActive?.() && childDocsActive
+            ? true
+            : undefined;
+    };
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return (
             <DocumentView
@@ -248,7 +250,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                 PanelWidth={width}
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
-                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                dropAction={StrCast(this.rootDoc.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={dxf}
@@ -257,6 +259,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                 hideResizeHandles={this.props.childHideResizeHandles?.()}
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 fitContentsToBox={this.props.fitContentsToBox}
+                dragAction={this.props.childDragAction}
                 focus={this.props.focus}
                 childFilters={this.childDocFilters}
                 childFiltersByRanges={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 15424de98..06b227b4b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -952,7 +952,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 styleProvider={DefaultStyleProvider}
                                 waitForDoubleClickToClick={returnNever}
                                 defaultDoubleClick={returnIgnore}
-                                enableDragWhenActive={true}
+                                dragAction="move"
                                 onClickScriptDisable="always"
                                 focus={this.props.schema.focusDocument}
                                 childFilters={this.props.schema.childDocFilters}
@@ -970,7 +970,6 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 hideLinkAnchors={true}
                                 layout_fitWidth={returnTrue}
                                 scriptContext={this}
-                                canEmbedOnDrag={true}
                             />
                         </div>
                     );
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 4f3503751..e8e606030 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -53,13 +53,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     onPointerEnter = (e: any) => {
-        if (!SnappingManager.GetIsDragging()) return;
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.addEventListener('pointermove', this.onPointerMove);
+        if (SnappingManager.GetIsDragging() && this.props.isContentActive()) {
+            document.removeEventListener('pointermove', this.onPointerMove);
+            document.addEventListener('pointermove', this.onPointerMove);
+        }
     };
 
     onPointerMove = (e: any) => {
-        if (!SnappingManager.GetIsDragging()) return;
         const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); //  this.schemaView?._selectedDocs.has(doc) ?? false;
 
         if (this._ref && dragIsRow) {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 97264508c..a958f53ea 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -76,7 +76,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'embed',
+            dragAction: 'move',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 5af05e491..9ceac3b8c 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -78,7 +78,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             e,
             e => {
                 const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'embed');
-                dragData.removeDropProperties = ['hidden'];
+                dragData.dropPropertiesToRemove = ['hidden'];
                 DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
                 return true;
             },
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 664a0fa4f..745efea08 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -70,7 +70,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         linkSearch={true}
                         linkCreated={this.props.linkCreated}
                         fieldKey="data"
-                        dropAction="move"
                         isSelected={returnTrue}
                         isContentActive={returnTrue}
                         select={returnTrue}
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 660045a6f..a12f1c12b 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -93,4 +93,4 @@
             display: flex;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index e6a4635c0..d747c4527 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -2,11 +2,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../../fields/Doc';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
+import { Docs, DocUtils } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
-import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
@@ -32,15 +31,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     get clipWidthKey() {
         return '_' + this.props.fieldKey + '_clipWidth';
     }
-
     componentDidMount() {
         this.props.setContentView?.(this);
     }
-
     protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
         this._disposers[disposerId]?.();
         if (ele) {
-            // create disposers identified by disposerId to remove drag & drop listeners
             this._disposers[disposerId] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.internalDrop(e, dropEvent, fieldKey), this.layoutDoc);
         }
     };
@@ -50,29 +46,40 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         if (dropEvent.complete.docDragData) {
             event.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
             const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
-            droppedDocs.lastElement().embedContainer = this.dataDoc;
-            this.dataDoc[fieldKey] = droppedDocs.lastElement();
+            const res = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.rootDoc, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey));
+            res && (droppedDocs.lastElement().embedContainer = this.dataDoc);
+            return res;
         }
     };
 
     private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => {
-        e.button !== 2 &&
+        if (e.button !== 2) {
             setupMoveUpEvents(
                 this,
                 e,
                 this.onPointerMove,
                 emptyFunction,
+                action((e, doubleTap) => {
+                    if (doubleTap) {
+                        this._isAnyChildContentActive = true;
+                        if (!this.dataDoc[this.fieldKey + '_1']) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
+                        if (!this.dataDoc[this.fieldKey + '_2']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
+                    }
+                }),
+                false,
+                undefined,
                 action(() => {
-                    // on click, animate slider movement to the targetWidth
+                    if (this._isAnyChildContentActive) return;
                     this._animating = 'all 200ms';
+                    // on click, animate slider movement to the targetWidth
                     this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth();
                     setTimeout(
                         action(() => (this._animating = '')),
                         200
                     );
-                }),
-                false
+                })
             );
+        }
     };
 
     @action
@@ -86,7 +93,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor = Docs.Create.ConfigDocument({
-            title: 'ImgAnchor:' + this.rootDoc.title,
+            title: 'CompareAnchor:' + this.rootDoc.title,
             // set presentation timing properties for restoring view
             presTransition: 1000,
             annotationOn: this.rootDoc,
@@ -105,12 +112,29 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         e.stopPropagation; // prevent click event action (slider movement) in registerSliding
         delete this.dataDoc[fieldKey];
     };
+    moveDoc = (doc: Doc, addDocument: (document: Doc | Doc[]) => boolean, which: string) => this.remDoc(doc, which) && addDocument(doc);
+    addDoc = (doc: Doc, which: string) => {
+        this.dataDoc[which] = doc;
+        return true;
+    };
+    remDoc = (doc: Doc, which: string) => {
+        if (this.dataDoc[which] === doc) {
+            this.dataDoc[which] = undefined;
+            return true;
+        }
+        return false;
+    };
+
+    whenChildContentsActiveChanged = action((isActive: boolean) => (this._isAnyChildContentActive = isActive));
 
     docStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         if (property === StyleProp.PointerEvents) return 'none';
         return this.props.styleProvider?.(doc, props, property);
     };
-
+    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);
+    remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true);
     render() {
         const clearButton = (which: string) => {
             return (
@@ -118,33 +142,35 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                     className={`clear-button ${which}`}
                     onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding
                     onClick={e => this.clearDoc(e, which)}>
-                    <FontAwesomeIcon className={`clear-button ${which}`} icon={'times'} size="sm" />
+                    <FontAwesomeIcon className={`clear-button ${which}`} icon="times" size="sm" />
                 </div>
             );
         };
         const displayDoc = (which: string) => {
-            const whichDoc = Cast(this.dataDoc[which], Doc, null);
-            // if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null);
-            const targetDoc = Cast(whichDoc?.annotationOn, Doc, null) ?? whichDoc;
-            return whichDoc ? (
+            const whichDoc = DocCast(this.dataDoc[which]);
+            const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
+            return targetDoc ? (
                 <>
                     <DocumentView
                         {...this.props}
+                        Document={targetDoc}
+                        DataDoc={undefined}
+                        moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2}
+                        removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
-                        isContentActive={returnFalse}
+                        isContentActive={emptyFunction}
                         isDocumentActive={returnFalse}
-                        styleProvider={this.docStyleProvider}
-                        Document={targetDoc}
-                        DataDoc={undefined}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        styleProvider={this._isAnyChildContentActive ? this.props.styleProvider : this.docStyleProvider}
                         hideLinkButton={true}
-                        pointerEvents={returnNone}
+                        pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
                     />
                     {clearButton(which)}
                 </> // placeholder image if doc is missing
             ) : (
                 <div className="placeholder">
-                    <FontAwesomeIcon className="upload-icon" icon={'cloud-upload-alt'} size="lg" />
+                    <FontAwesomeIcon className="upload-icon" icon="cloud-upload-alt" size="lg" />
                 </div>
             );
         };
@@ -157,7 +183,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         };
 
         return (
-            <div className={`comparisonBox${this.props.isContentActive() || SnappingManager.GetIsDragging() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
+            <div className={`comparisonBox${this.props.isContentActive() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
                 {displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)}
                 <div className="clip-div" style={{ width: this.clipWidth + '%', transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
                     {displayBox(`${this.fieldKey}_1`, 0, 0)}
@@ -169,7 +195,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                         left: `calc(${this.clipWidth + '%'} - 0.5px)`,
                         cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
                     }}
-                    onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
+                    onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
                 >
                     <div className="slide-handle" />
                 </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 23148ed34..5470a72f5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -14,7 +14,7 @@ import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, return18, returnEmptyString, returnFalse, returnTrue, returnVal, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { DocServer } from '../../DocServer';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -47,6 +47,7 @@ import { DocumentLinksButton } from './DocumentLinksButton';
 import './DocumentView.scss';
 import { FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { KeyValueBox } from './KeyValueBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { PresEffect, PresEffectDirection } from './trails';
 import { PinProps, PresBox } from './trails/PresBox';
@@ -163,6 +164,7 @@ export interface DocumentViewSharedProps {
     docViewPath: () => DocumentView[];
     childHideDecorationTitle?: () => boolean;
     childHideResizeHandles?: () => boolean;
+    childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
     dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
     styleProvider: Opt<StyleProviderFunc>;
     setTitleFocus?: () => void;
@@ -182,7 +184,7 @@ export interface DocumentViewSharedProps {
     pinToPres: (document: Doc, pinProps: PinProps) => void;
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    canEmbedOnDrag?: boolean;
+    dragAction?: dropActionType;
     treeViewDoc?: Doc;
     xPadding?: number;
     yPadding?: number;
@@ -194,7 +196,6 @@ export interface DocumentViewSharedProps {
     forceAutoHeight?: boolean;
     disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
     onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
-    enableDragWhenActive?: boolean;
     waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
     defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
     pointerEvents?: () => Opt<string>;
@@ -314,7 +315,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get titleHeight() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
     }
-    @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
+    get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
         return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
     }
     @computed get finalLayoutKey() {
@@ -355,11 +356,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     componentDidMount() {
         this.setupHandlers();
     }
-
+    preDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
+        if (de.complete.docDragData && this.isContentActive()) {
+            targetAction && (de.complete.docDragData.dropAction = targetAction);
+            e.stopPropagation();
+        }
+    };
     setupHandlers() {
         this.cleanupHandlers(false);
         if (this._mainCont.current) {
-            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document);
+            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document, this.preDropFunc);
             this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this));
             this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this));
         }
@@ -387,7 +393,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.treeViewDoc = this.props.treeViewDoc;
             dragData.removeDocument = this.props.removeDocument;
             dragData.moveDocument = this.props.moveDocument;
-            dragData.canEmbed = this.props.canEmbedOnDrag;
+            dragData.canEmbed = this.rootDoc.dragAction ?? this.props.dragAction ? true : false;
             const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
             ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
             DragManager.StartDocumentDrag(
@@ -478,7 +484,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             : '';
                         DocumentViewInternal.addDocTabFunc = oldFunc;
                     };
-                    clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title));
+                    clickFunc = () => UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title);
                 } else {
                     // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
@@ -506,7 +512,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     onPointerDown = (e: React.PointerEvent): void => {
         this._longPressSelector = setTimeout(() => {
             if (DocumentView.LongPress) {
-                if (this.rootDoc.dontUndo) {
+                if (this.rootDoc.undoIgnoreFields) {
                     runInAction(() => (UndoStack.HideInline = !UndoStack.HideInline));
                 } else {
                     this.props.select(false);
@@ -535,7 +541,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._type_collection !== CollectionViewType.Docking) e.preventDefault();
 
                 // listen to move events if document content isn't active or document is draggable
-                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || this.props.enableDragWhenActive || this.rootDoc.enableDragWhenActive)) {
+                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || this.props.dragAction || this.rootDoc.dragAction)) {
                     document.addEventListener('pointermove', this.onPointerMove);
                 }
             }
@@ -549,7 +555,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
             this.cleanupPointerEvents();
-            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
+            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dragAction || this.props.dragAction || undefined) as dropActionType));
         }
     };
 
@@ -608,10 +614,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return;
+        if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false;
         if (this.props.Document === Doc.ActiveDashboard) {
+            e.stopPropagation();
             alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
-            return;
+            e.preventDefault();
+            return true;
         }
         const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
         if (linkdrag) {
@@ -625,8 +633,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                 }
+                return true;
             }
         }
+        return false;
     };
 
     @undoBatch
@@ -851,7 +861,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setHeight = (height: number) => (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
     isContentActive = (outsideReaction?: boolean): boolean | undefined => {
-        return this.props.isContentActive() === false
+        //  true  - if the document has been activated directly or indirectly (by having its children selected)
+        //  false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
+        // undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking)
+        return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' || (this.rootDoc.pointerEvents === 'none' && !StrCast(this.props.LayoutTemplateString).includes(KeyValueBox.name))
             ? false
             : Doc.ActiveTool !== InkTool.None ||
               SnappingManager.GetIsDragging() ||
@@ -871,17 +884,18 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
         const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
         return !this.props.LayoutTemplateString &&
-            !this.props.isSelected() &&
+            !this.isContentActive() &&
             LightboxView.LightboxDoc !== this.rootDoc &&
             this.thumb &&
             !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
-            ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking) &&
-            !this._componentView?.isAnyChildContentActive?.()
+            ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking)
             ? true
             : false;
     };
     childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
-    contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents);
+
+    /// disable pointer events on content when there's an enabled onClick script, or if contents are marked inactive
+    contentPointerEvents = () => ((!this.disableClickScriptFunc && this.onClickHandler) || this.isContentActive() === false ? 'none' : this.pointerEvents);
     @computed get contents() {
         TraceMobx();
         const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString;
@@ -889,7 +903,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             <div
                 className="documentView-contentsView"
                 style={{
-                    pointerEvents: (isInk ? 'none' : this.pointerEvents) ?? 'all',
+                    pointerEvents: (isInk ? 'none' : this.contentPointerEvents()) ?? 'all',
                     height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
                 }}>
                 {!this._retryThumb || !this.thumbShown() ? null : (
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 85dd779fc..4ebf22ddf 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -45,7 +45,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldType: { name: string }, fieldStr: string) {
-        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
+        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={'data'} />"
     }
 
     @computed
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 9acbee1e7..64d6814df 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -387,7 +387,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     ref={this._overlayIconRef}
                     onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
                     style={{
-                        display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
+                        display: (this.props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
                         width: 'min(10%, 25px)',
                         height: 'min(10%, 25px)',
                         background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
@@ -505,7 +505,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     }
                 })}
                 style={{
-                    display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
+                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
                     width: this.props.PanelWidth() ? undefined : `100%`,
                     height: this.props.PanelWidth() ? undefined : `100%`,
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 5b6b0b5a7..a6712a3db 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -213,18 +213,18 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         document.addEventListener('pointerup', this.onDividerUp);
     };
 
-    getFieldView = async () => {
+    getFieldView = () => {
         const rows = this.rows.filter(row => row.isChecked);
         if (rows.length > 1) {
-            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true });
+            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document).title}`, _chromeHidden: true });
             for (const row of rows) {
-                const field = this.createFieldView(DocCast(this.props.Document.data), row);
+                const field = this.createFieldView(DocCast(this.props.Document), row);
                 field && Doc.AddDocToList(parent, 'data', field);
                 row.uncheck();
             }
             return parent;
         }
-        return rows.length ? this.createFieldView(DocCast(this.props.Document.data), rows.lastElement()) : undefined;
+        return rows.length ? this.createFieldView(DocCast(this.props.Document), rows.lastElement()) : undefined;
     };
 
     createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index d0db60b37..01acdccb7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -67,7 +67,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'embed',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index e86b881a8..9bcd04cf5 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -47,7 +47,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.rootDoc]);
                 dragData.dropAction = 'embed';
-                dragData.removeDropProperties = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
+                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 {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 93e54ffb7..de0b57fd7 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -512,9 +512,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.addDocument(doc, annotationKey);
     };
 
-    pointerEvents = () => {
-        return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
-    };
+    pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
+
     @computed get annotationLayer() {
         return (
             <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 72f37b62c..a9154c5bb 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -510,7 +510,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     pointerEvents = () => {
-        return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
+        return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
     };
     @computed get annotationLayer() {
         return (
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6d040ca1a..fd4c6366b 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -559,7 +559,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </div>
         );
     }
-    isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected() || (this.props.renderDepth === 0 && LightboxView.IsLightboxDocView(this.props.docViewPath()));
     @computed get renderPdfView() {
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
@@ -592,7 +591,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         pdf={this._pdf}
                         focus={this.focus}
                         url={this.pdfUrl!.url.pathname}
-                        isContentActive={this.isPdfContentActive}
                         anchorMenuClick={this.anchorMenuClick}
                         loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
                         setPdfViewer={this.setPdfViewer}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 16705a6b3..34a1229ba 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -918,7 +918,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 ScreenToLocalTransform={this.scrollXf}
                 NativeDimScaling={returnOne}
                 focus={this.focus}
-                dropAction="embed"
                 childFilters={childFilters}
                 select={emptyFunction}
                 isAnyChildContentActive={returnFalse}
@@ -1003,8 +1002,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         return this.props.styleProvider?.(doc, props, property);
     };
-    pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance?.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
-    annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none');
+    pointerEvents = () =>
+        !this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
+            ? 'all' //
+            : 'none';
+    annotationPointerEvents = () => (this.props.isContentActive() && (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
     render() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
@@ -1013,7 +1015,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             <div
                 className="webBox"
                 ref={this._mainCont}
-                style={{ pointerEvents: this.pointerEvents(), position: SnappingManager.GetIsDragging() ? 'absolute' : undefined, display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined }}>
+                style={{
+                    pointerEvents: this.pointerEvents(), //
+                    position: SnappingManager.GetIsDragging() ? 'absolute' : undefined,
+                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
+                }}>
                 <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} />
                 <div
                     className="webBox-container"
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 079129dc9..2cd3c6cd6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -297,7 +297,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     dispatchTransaction = (tx: Transaction) => {
-        if (this._editorView) {
+        if (this._editorView && (this._editorView as any).docView) {
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
 
@@ -2020,17 +2020,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     _oldWheel: any;
     render() {
         TraceMobx();
-        const active = this.props.isContentActive() || this.props.isSelected();
-        const selected = active;
+        const active = this.props.isContentActive();
         const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
         const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
-        const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
-        if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
+        if (!active && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
         const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-        const selPad = (selected && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
-        const selPaddingClass = selected && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
+        const selPad = (active && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
+        const selPaddingClass = active && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
         const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
         return styleFromLayoutString?.height === '0px' ? null : (
             <div
@@ -2051,7 +2049,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                               width: `${100 / scale}%`,
                               height: `${100 / scale}%`,
                           }),
-                    display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
+                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
                     transition: 'inherit',
                     // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
                     color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
@@ -2066,7 +2064,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     style={{
                         overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
                         height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
-                        pointerEvents: interactive ? undefined : 'none',
+                        pointerEvents: Doc.ActiveTool === InkTool.None ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
                     onKeyDown={this.onKeyDown}
@@ -2078,11 +2076,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     onPointerDown={this.onPointerDown}
                     onDoubleClick={this.onDoubleClick}>
                     <div
-                        className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
+                        className={`formattedTextBox-outer${active ? '-selected' : ''}`}
                         ref={this._scrollRef}
                         style={{
                             width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
-                            pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
                             overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
                         }}
                         onScroll={this.onScroll}
@@ -2096,7 +2093,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                                 paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
                                 paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
                                 paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
-                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined) : undefined,
+                                // pointerEvents: !active && IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined,
                             }}
                         />
                     </div>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 4f7d2e1c1..ef1c3911c 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1029,9 +1029,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive(outsideReaction);
-    //.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
-
     /**
      * For sorting the array so that the order is maintained when it is dropped.
      */
@@ -2462,6 +2459,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 childIgnoreNativeSize={true}
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
+                                childDragAction="move"
                                 //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 34e069046..f31cf6147 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -192,13 +192,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const dragArray = this.presBoxView?._dragArray ?? [];
         const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
         if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
-        dragData.dropAction = 'move';
         dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc;
         dragData.moveDocument = this.props.moveDocument;
         const dragItem: HTMLElement[] = [];
+        const classesToRestore = new Map<HTMLElement, string>();
         if (dragArray.length === 1) {
             const doc = this._itemRef.current || dragArray[0];
             if (doc) {
+                classesToRestore.set(doc, doc.className);
                 doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
                 dragItem.push(doc);
             }
@@ -212,16 +213,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             dragItem.push(doc);
         }
 
-        // const dropEvent = () => runInAction(() => this._dragging = false);
         if (activeItem) {
+            runInAction(() => (this._dragging = true));
             DragManager.StartDocumentDrag(
                 dragItem.map(ele => ele),
                 dragData,
                 e.clientX,
                 e.clientY,
-                undefined
+                undefined,
+                action(() => {
+                    Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1]));
+                    this._dragging = false;
+                })
             );
-            // runInAction(() => this._dragging = true);
             return true;
         }
         return false;
@@ -536,7 +540,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             <div
                                 className="presItem-number"
                                 title="select without navigation"
-                                style={{ pointerEvents: this.presBoxView?.isContentActive() ? 'all' : undefined }}
                                 onPointerDown={e => {
                                     e.stopPropagation();
                                     if (this._itemRef.current && this._dragRef.current) {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index dd202418b..8f6b8cd41 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -483,7 +483,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
     };
 
-    pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
+    pointerEvents = () =>
+        this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown()
+            ? 'all' //
+            : 'none';
     @computed get annotationLayer() {
         return (
             <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
@@ -514,10 +517,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
-            if (this.inlineTextAnnotations.includes(doc)) return 'none';
+            if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none';
             return 'all';
         }
         return this.props.styleProvider?.(doc, props, property);
@@ -536,8 +539,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
                 setContentView={emptyFunction} // override setContentView to do nothing
-                pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it.
-                childPointerEvents="all" // but freeform children need to get events to allow text editing, etc
+                pointerEvents={SnappingManager.GetIsDragging() && this.props.isContentActive() ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it.
+                childPointerEvents={this.props.isContentActive() !== false ? 'all' : 'none'} // but freeform children need to get events to allow text editing, etc
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
                 fieldKey={this.props.fieldKey + '_annotations'}
@@ -549,7 +552,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 ScreenToLocalTransform={this.overlayTransform}
                 isAnyChildContentActive={returnFalse}
                 isAnnotationOverlayScrollable={true}
-                dropAction="embed"
                 childFilters={childFilters}
                 select={emptyFunction}
                 bringToFront={emptyFunction}
@@ -558,14 +560,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
         </div>
     );
     @computed get overlayTransparentAnnotations() {
-        return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length ? 'none' : undefined);
+        return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length && this.props.isContentActive() ? 'none' : undefined);
     }
     @computed get overlayOpaqueAnnotations() {
         return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined);
     }
     @computed get overlayLayer() {
         return (
-            <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none' }}>
+            <div style={{ pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : 'none' }}>
                 {this.overlayTransparentAnnotations}
                 {this.overlayOpaqueAnnotations}
             </div>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index c674a20d2..c9f7e4114 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -776,7 +776,7 @@ export namespace Doc {
                 linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
             }
         });
-        Doc.SetInPlace(copy, 'title', 'CLONE: ' + doc.title, true);
+        Doc.SetInPlace(copy, 'title', '>:' + doc.title, true);
         copy.cloneOf = doc;
         cloneMap.set(doc[Id], copy);
 
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 757d507be..76b287be7 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -95,12 +95,12 @@ export const documentSchema = createSchema({
     link_displayArrow: 'boolean', // toggles directed arrows
 
     // drag drop properties
-    _stayInCollection: 'boolean', // whether document can be dropped into a different collection
+    _dragOnlyWithinContainer: 'boolean', // whether document can be dropped into a different collection
     dragFactory: Doc, // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    dropAction: 'string', // override specifying what should happen when this document is dropped (can be "embed", "copy", "move")
-    targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("embed", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
-    childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
-    removeDropProperties: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
+    dropAction: 'string', // override specifying what should happen when something is dropped on this document (can be "embed", "copy", "move")
+    dragAction: 'string', // override specifying what should happen when this document s dragged (can be "embed", "copy", "move")
+    childDragAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
+    dropPropertiesToRemove: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
 });
 
 export const collectionSchema = createSchema({
diff --git a/src/fields/util.ts b/src/fields/util.ts
index f365adf4b..e3d1abc53 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -7,7 +7,7 @@ import { SerializationHelper } from '../client/util/SerializationHelper';
 import { UndoManager } from '../client/util/UndoManager';
 import { returnZero } from '../Utils';
 import CursorField from './CursorField';
-import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, updateCachedAcls } from './Doc';
+import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, StrListCast, updateCachedAcls } from './Doc';
 import { AclAdmin, AclEdit, AclPrivate, AclSelfEdit, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, Update, UpdatingFromServer, Width } from './DocSymbols';
 import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols';
 import { List } from './List';
@@ -86,7 +86,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
             DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
         }
         !receiver[Initializing] &&
-            !receiver.dontUndo &&
+            !StrListCast(receiver.undoIgnoreFields).includes(prop.toString()) &&
             (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
             UndoManager.AddEvent(
                 {
-- 
cgit v1.2.3-70-g09d2

