From 33299a19d86948051eef442825c0b3241d1ac619 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 24 Apr 2023 11:55:53 -0400
Subject: fixed isContentActive=false to apply to stacking collections. fixed
 pile view to be faster and to work in fit content panels. fixed issues with
 hidden docs and freeformviews that act as lightboxes - hidden docs can be
 shown as the lightbox doc without modifying the hidden flag to allow
 collection state to be restored.

---
 .../CollectionFreeFormLayoutEngines.tsx            | 23 +++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 81b0c4d8a..2549ee0b3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -93,6 +93,7 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
             width: layout[WidthSym](),
             height: layout[HeightSym](),
             pair: { layout, data },
+            transition: 'all .3s',
             replica: '',
         });
     });
@@ -100,28 +101,27 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
 }
 
 export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
-    const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
     const docMap = new Map<string, PoolData>();
-    const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75
-    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
-    const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
+    const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)];
     childPairs.forEach(({ layout, data }, i) => {
-        const docSize = layout.layoutKey === 'layout_icon' ? (mustFit ? panelDim[0] * 0.33 : 75) : 400; // assume a icon sized at 75
+        const aspect = layout[HeightSym]() / layout[WidthSym]();
+        const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect);
         const deg = (i / childPairs.length) * Math.PI * 2;
         docMap.set(layout[Id], {
-            x: Math.cos(deg) * burstRadius[0] - docSize / 2,
-            y: Math.sin(deg) * burstRadius[1] - (docSize * layout[HeightSym]()) / layout[WidthSym]() / 2,
-            width: docSize, //layout[WidthSym](),
-            height: (docSize * layout[HeightSym]()) / layout[WidthSym](),
+            x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)),
+            y: Math.min(burstDiam[1] / 2 - docSize * aspect, Math.max(-burstDiam[1] / 2, (Math.sin(deg) * burstDiam[1]) / 2 - (docSize / 2) * aspect)),
+            width: docSize,
+            height: docSize * aspect,
             zIndex: NumCast(layout.zIndex),
             pair: { layout, data },
             replica: '',
             color: 'white',
             backgroundColor: 'white',
+            transition: 'all 0.3s',
         });
     });
-    const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
-    return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
+    const divider = { type: 'div', color: 'transparent', x: -burstDiam[0] / 2, y: -burstDiam[1] / 2, width: 15, height: 15, payload: undefined };
+    return normalizeResults(burstDiam, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
 export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
@@ -424,6 +424,7 @@ function normalizeResults(
                     color: newPosRaw.color,
                     pair: ele[1].pair,
                 };
+                if (newPosRaw.transition) newPos.transition = newPosRaw.transition;
                 poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos });
             }
         });
-- 
cgit v1.2.3-70-g09d2


From c83c44313b0d3e6168408fadd0c01d99057c325a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 24 Apr 2023 12:45:38 -0400
Subject: fixed pile view to be resizable.

---
 src/client/views/collections/CollectionPileView.tsx                 | 6 ++++--
 .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx          | 3 ++-
 src/client/views/nodes/ImageBox.tsx                                 | 2 +-
 3 files changed, 7 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index e5d89865b..5b96a8682 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -73,6 +73,9 @@ export class CollectionPileView extends CollectionSubView() {
     // toggles the pileup between starburst to compact
     toggleStarburst = action(() => {
         if (this.layoutEngine() === computeStarburstLayout.name) {
+            if (this.rootDoc[WidthSym]() !== NumCast(this.rootDoc._starburstDiameter, 500)) {
+                this.rootDoc._starburstDiameter = this.rootDoc[WidthSym]();
+            }
             const defaultSize = 110;
             this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2;
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
@@ -83,8 +86,7 @@ export class CollectionPileView extends CollectionSubView() {
             this.layoutDoc._panY = -10;
             this.props.Document._pileLayoutEngine = computePassLayout.name;
         } else {
-            const defaultSize = 500;
-            !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5);
+            const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500);
             this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2;
             this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym]();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 2549ee0b3..c1f3c5aa6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -103,9 +103,10 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
 export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
     const docMap = new Map<string, PoolData>();
     const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)];
+    const burstScale = NumCast(pivotDoc._starburstDocScale, 1);
     childPairs.forEach(({ layout, data }, i) => {
         const aspect = layout[HeightSym]() / layout[WidthSym]();
-        const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect);
+        const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect) * burstScale;
         const deg = (i / childPairs.length) * Math.PI * 2;
         docMap.set(layout[Id], {
             x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)),
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 20e5f5eca..c9be10d3a 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -407,7 +407,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             .filter(url => url)
             .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
-        return paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
+        return paths.length ? paths : [Utils.CorsProxy('https://cs.brown.edu/~bcz/noImage.png')];
     }
 
     @observable _isHovering = false; // flag to switch between primary and alternate images on hover
-- 
cgit v1.2.3-70-g09d2


From 2255f2ffd4d9c5818d8d26f1814b315ad914eaa9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 8 May 2023 11:34:29 -0400
Subject: fixed dragging inkMask strokes.  fixed background color/fill for
 strokes.  fixed send to back.  changed #tags to be a list

---
 src/client/views/FilterPanel.tsx                   |   4 +-
 src/client/views/PropertiesView.tsx                | 150 +++++++--------------
 src/client/views/StyleProvider.tsx                 |   4 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  15 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   5 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   7 +-
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  20 +--
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +
 .../views/nodes/formattedText/RichTextRules.ts     |  10 +-
 src/fields/Doc.ts                                  |   3 +-
 11 files changed, 85 insertions(+), 137 deletions(-)

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

diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index a237249c1..d17a4ea25 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -145,8 +145,8 @@ export class FilterPanel extends React.Component<filterProps> {
         const set = new Set<string>([String.fromCharCode(127) + '--undefined--']);
         if (facetHeader === 'tags')
             allCollectionDocs.forEach(child =>
-                Field.toString(child[facetHeader] as Field)
-                    .split(':')
+                StrListCast(child[facetHeader])
+                    .filter(h => h)
                     .forEach(key => set.add(key))
             );
         else
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 6582c3f2a..fff8390b3 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -7,7 +7,7 @@ import { intersection } from 'lodash';
 import { action, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { AclAdmin, AclSym, DataSym, Doc, Field, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -152,116 +152,55 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return 0;
     };
 
-    @computed get expandedField() {
+    editableFields = (filter: (key: string) => boolean, reqdKeys: string[]) => {
+        const rows: JSX.Element[] = [];
         if (this.dataDoc && this.selectedDoc) {
-            const ids: { [key: string]: string } = {};
-            const docs = SelectionManager.Views().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
-            docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
-            const rows: JSX.Element[] = [];
-            for (const key of Object.keys(ids).slice().sort()) {
-                const docvals = new Set<any>();
-                docs.forEach(doc => docvals.add(doc[key]));
-                const contents = Array.from(docvals.keys()).length > 1 ? '-multiple' : docs[0][key];
-                if (key[0] === '#') {
-                    rows.push(
-                        <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '2px' }} key={key}>
-                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key}</span>
-                            &nbsp;
-                        </div>
-                    );
-                } else {
-                    const contentElement = (
-                        <EditableView
-                            key="editableView"
-                            contents={contents !== undefined ? Field.toString(contents as Field) : 'null'}
-                            height={13}
-                            fontSize={10}
-                            GetValue={() => (contents !== undefined ? Field.toString(contents as Field) : 'null')}
-                            SetValue={(value: string) => {
-                                docs.map(doc => KeyValueBox.SetField(doc, key, value, true));
-                                return true;
-                            }}
-                        />
-                    );
-                    rows.push(
-                        <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
-                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
-                            &nbsp;
-                            {contentElement}
-                        </div>
-                    );
-                }
-            }
+            const ids = new Set<string>(reqdKeys);
+            const docs: Doc[] = SelectionManager.Views().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
+            docs.forEach(doc => Object.keys(doc).forEach(key => doc[key] !== ComputedField.undefined && ids.add(key)));
+
+            // prettier-ignore
+            Array.from(ids).filter(filter).sort().map(key => {
+                const multiple = Array.from(docs.reduce((set,doc) => set.add(doc[key]), new Set<FieldResult>()).keys()).length > 1;
+                const editableContents = multiple ? '-multiple-' : Field.toKeyValueString(docs[0], key);
+                const displayContents = multiple ? '-multiple-' : Field.toString(docs[0][key] as Field);
+                const contentElement = key[0] === '#'  ? <></> : (
+                    <EditableView
+                        key="editableView"
+                        contents={displayContents}
+                        height={13}
+                        fontSize={10}
+                        GetValue={() => editableContents}
+                        SetValue={(value: string) => {
+                            value !== '-multiple-' && docs.map(doc => KeyValueBox.SetField(doc, key, value, true));
+                            return true;
+                        }}
+                    />);
+                rows.push(
+                    <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
+                        <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
+                        &nbsp;
+                        {contentElement}
+                    </div>
+                );
+            });
+
             rows.push(
-                <div className="propertiesView-field" key={'newKeyValue'} style={{ marginTop: '3px' }}>
+                <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px' }}>
                     <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
                 </div>
             );
-            return rows;
         }
+        return rows;
+    };
+
+    @computed get expandedField() {
+        return this.editableFields(returnTrue, []);
     }
 
     @computed get noviceFields() {
-        if (this.dataDoc) {
-            const ids: { [key: string]: string } = {};
-            const docs = SelectionManager.Views().length < 2 ? [this.dataDoc] : SelectionManager.Views().map(dv => dv.dataDoc);
-            docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
-            const rows: JSX.Element[] = [];
-            const noviceReqFields = ['author', 'creationDate', 'tags'];
-            const noviceLayoutFields = ['_curPage'];
-            const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl'))), ...noviceReqFields, ...noviceLayoutFields];
-            for (const key of noviceKeys.sort()) {
-                const docvals = new Set<any>();
-                docs.forEach(doc => docvals.add(doc[key]));
-                const contents = Array.from(docvals.keys()).length > 1 ? '-multiple' : docs[0][key];
-                if (key[0] === '#') {
-                    rows.push(
-                        <div className="propertiesView-uneditable-field" key={key}>
-                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key}</span>
-                            &nbsp;
-                        </div>
-                    );
-                } else if (contents !== undefined) {
-                    const value = Field.toString(contents as Field);
-                    if (noviceReqFields.includes(key) || key.indexOf('lastModified') !== -1) {
-                        rows.push(
-                            <div className="propertiesView-uneditable-field" key={key}>
-                                <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ': '}</span>
-                                <div style={{ whiteSpace: 'nowrap', overflowX: 'hidden' }}>{value}</div>
-                            </div>
-                        );
-                    } else {
-                        const contentElement = (
-                            <EditableView
-                                key="editableView"
-                                contents={value}
-                                height={13}
-                                fontSize={10}
-                                GetValue={() => (contents !== undefined ? Field.toString(contents as Field) : 'null')}
-                                SetValue={(value: string) => {
-                                    docs.map(doc => KeyValueBox.SetField(doc, key, value, true));
-                                    return true;
-                                }}
-                            />
-                        );
-
-                        rows.push(
-                            <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
-                                <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
-                                &nbsp;
-                                {contentElement}
-                            </div>
-                        );
-                    }
-                }
-            }
-            rows.push(
-                <div className="propertiesView-field" key={'newKeyValue'} style={{ marginTop: '3px' }}>
-                    <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
-                </div>
-            );
-            return rows;
-        }
+        const noviceReqFields = ['author', 'creationDate', 'tags', '_curPage'];
+        return this.editableFields(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields);
     }
 
     @undoBatch
@@ -280,9 +219,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             } else if (value[0] === '#') {
                 const newVal = value + `:'${value}'`;
                 doc[DataSym][value] = value;
-                const tags = StrCast(doc.tags, ':');
-                if (!tags.includes(`${value}:`)) {
-                    doc[DataSym].tags = `${tags + value + ':'}`;
+                const tags = StrListCast(doc.tags);
+                if (!tags.includes(value)) {
+                    tags.push(value);
+                    doc[DataSym].tags = tags.length ? new List<string>(tags) : undefined;
                 }
                 return true;
             }
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index d13052f71..192a501f2 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -303,13 +303,13 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             }
         }
         case StyleProp.PointerEvents:
-            const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
+            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?.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 || doc?.isInkMask) return 'none';
+            if (opacity() === 0) return 'none';
             if (props?.isDocumentActive?.()) return isInk ? 'visiblePainted' : 'all';
             return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active
         case StyleProp.Decorations:
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index c1f3c5aa6..fee4705e6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -133,20 +133,15 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
     let nonNumbers = 0;
     const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author';
     childPairs.map(pair => {
-        const lval =
-            pivotFieldKey === '#' || pivotFieldKey === 'tags'
-                ? Array.from(Object.keys(Doc.GetProto(pair.layout)))
-                      .filter(k => k.startsWith('#'))
-                      .map(k => k.substring(1))
-                : Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
+        const listValue = Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
 
         const num = toNumber(pair.layout[pivotFieldKey]);
         if (num === undefined || Number.isNaN(num)) {
             nonNumbers++;
         }
         const val = Field.toString(pair.layout[pivotFieldKey] as Field);
-        if (lval) {
-            lval.forEach((val, i) => {
+        if (listValue) {
+            listValue.forEach((val, i) => {
                 !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
                 pivotColumnGroups.get(val)!.docs.push(pair.layout);
                 pivotColumnGroups.get(val)!.replicas.push(i.toString());
@@ -159,8 +154,8 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
             docMap.set(pair.layout[Id], {
                 x: 0,
                 y: 0,
-                zIndex: -99,
-                width: 0,
+                zIndex: 0,
+                width: 0, // should make doc hidden in CollectionFreefromDocumentView
                 height: 0,
                 pair,
                 replica: '',
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9cc732008..25da868e0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1158,7 +1158,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     bringToFront = (doc: Doc, sendToBack?: boolean) => {
         if (sendToBack) {
-            doc.zIndex = 0;
+            const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
+            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) {
             doc.zIndex = 5000;
         } else {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 24b9f3b25..b68bcc263 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -16,6 +16,8 @@ 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;
@@ -201,9 +203,10 @@ 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;
         return (
             <div
-                className={'collectionFreeFormDocumentView-container'}
+                className="collectionFreeFormDocumentView-container"
                 style={{
                     width: this.panelWidth(),
                     height: this.panelHeight(),
@@ -211,7 +214,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     transformOrigin: '50% 50%',
                     transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
                     zIndex: this.ZInd,
-                    display: this.ZInd === -99 ? 'none' : undefined,
+                    pointerEvents: isInk ? 'none' : undefined,
                 }}>
                 {this.props.renderCutoffProvider(this.props.Document) ? (
                     <div style={{ position: 'absolute', width: this.panelWidth(), height: this.panelHeight(), background: 'lightGreen' }} />
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3bdd2bf6e..f1f5f7e10 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -870,7 +870,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents);
     @computed get contents() {
         TraceMobx();
-        const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name);
+        const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString;
         return (
             <div
                 className="documentView-contentsView"
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index f04678d8b..26515da30 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -224,12 +224,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                             </div>
                             <div
                                 className="dropbox-background"
-                                onClick={e => {
+                                onClick={action(e => {
                                     e.stopPropagation();
                                     this.rootDoc.dropDownOpen = false;
                                     this.noTooltip = false;
                                     Doc.UnBrushAllDocs();
-                                }}
+                                })}
                             />
                         </div>
                     ) : null}
@@ -336,11 +336,11 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : 'flex' }}
                 onClick={
                     dropdown
-                        ? () => {
+                        ? action(() => {
                               this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
                               this.noTooltip = this.rootDoc.dropDownOpen;
                               Doc.UnBrushAllDocs();
-                          }
+                          })
                         : undefined
                 }>
                 {dropdown ? null : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
@@ -358,12 +358,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                         </div>
                         <div
                             className="dropbox-background"
-                            onClick={e => {
+                            onClick={action(e => {
                                 e.stopPropagation();
                                 this.rootDoc.dropDownOpen = false;
                                 this.noTooltip = false;
                                 Doc.UnBrushAllDocs();
-                            }}
+                            })}
                         />
                     </div>
                 ) : null}
@@ -582,17 +582,19 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
     } else if (selectedViews.length) {
         if (checkResult) {
             const selView = selectedViews.lastElement();
+            const fieldKey = selView.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
             const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
             const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
-            return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber).backgroundColor ?? 'transparent';
+            return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber)[fieldKey] ?? 'transparent';
         }
         selectedViews.forEach(dv => {
+            const fieldKey = dv.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
             const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
             const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
             if (contentFrameNumber !== undefined) {
-                CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { backgroundColor: color });
+                CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color });
             } else {
-                dv.rootDoc._backgroundColor = color;
+                dv.rootDoc['_' + fieldKey] = color;
             }
         });
     } else {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index eb28ffbd7..1d668d6a9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -69,6 +69,7 @@ import { SummaryView } from './SummaryView';
 import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
 import { RTFMarkup } from '../../../util/RTFMarkup';
+import { List } from '../../../../fields/List';
 const translateGoogleApi = require('translate-google-api');
 export const GoogleRef = 'googleDocId';
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -318,6 +319,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 const removed = curTags.filter(tag => !accumTags.includes(tag));
                 removed.forEach(r => (dataDoc[r] = undefined));
                 added.forEach(a => (dataDoc[a] = a));
+                dataDoc.tags = curTags.length ? new List<string>(curTags) : undefined;
 
                 let unchanged = true;
                 if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index cc19d12bd..68b209332 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -1,7 +1,8 @@
 import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from 'prosemirror-inputrules';
 import { NodeSelection, TextSelection } from 'prosemirror-state';
-import { DataSym, Doc } from '../../../../fields/Doc';
+import { DataSym, Doc, StrListCast } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { normalizeEmail } from '../../../../fields/util';
@@ -325,9 +326,10 @@ export class RichTextRules {
                 const tag = match[1];
                 if (!tag) return state.tr;
                 this.Document[DataSym]['#' + tag] = '#' + tag;
-                const tags = StrCast(this.Document[DataSym].tags, ':');
-                if (!tags.includes(`#${tag}:`)) {
-                    this.Document[DataSym].tags = `${tags + '#' + tag + ':'}`;
+                const tags = StrListCast(this.Document[DataSym].tags);
+                if (!tags.includes(tag)) {
+                    tags.push(tag);
+                    this.Document[DataSym].tags = new List<string>(tags);
                 }
                 const fieldView = state.schema.nodes.dashField.create({ fieldKey: '#' + tag });
                 return state.tr
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index a9be24c8b..03ae91c50 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1387,7 +1387,8 @@ export namespace Doc {
         if (typeof value === 'string') {
             value = value.replace(`,${Utils.noRecursionHack}`, '');
         }
-        const fieldVal = key === '#' ? (StrCast(doc.tags).includes(':#' + value + ':') ? StrCast(doc.tags) : undefined) : doc[key];
+        const tagsString = doc.tags ? ':' + StrListCast(doc.tags).join(':') + ':' : '';
+        const fieldVal = key === '#' ? (tagsString.includes(':#' + value + ':') ? tagsString : undefined) : doc[key];
         if (Cast(fieldVal, listSpec('string'), []).length) {
             const vals = Cast(fieldVal, listSpec('string'), []);
             const docs = vals.some(v => (v as any) instanceof Doc);
-- 
cgit v1.2.3-70-g09d2


From 562e965cbc3d7629014ad3902e1177d5cbefd57c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 6 Jun 2023 18:48:59 -0400
Subject: updated Symbol naming.  fixed various compile errors related to type
 checking.

---
 src/client/documents/Documents.ts                  |   3 +-
 src/client/util/DictationManager.ts                |   4 +-
 src/client/util/DocumentManager.ts                 |   3 +-
 src/client/util/LinkManager.ts                     |  17 +-
 src/client/util/SharingManager.tsx                 |  23 ++-
 src/client/views/DocComponent.tsx                  |  14 +-
 src/client/views/DocumentDecorations.tsx           |   9 +-
 src/client/views/InkTranscription.tsx              |  11 +-
 src/client/views/InkingStroke.tsx                  |  11 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/OverlayView.tsx                   |   9 +-
 src/client/views/PropertiesView.tsx                |  29 +--
 src/client/views/collections/CollectionMenu.tsx    |   4 +-
 .../views/collections/CollectionNoteTakingView.tsx |  11 +-
 .../views/collections/CollectionPileView.tsx       |  19 +-
 .../views/collections/CollectionStackingView.tsx   |  13 +-
 .../views/collections/CollectionTreeView.tsx       |  19 +-
 src/client/views/collections/TabDocView.tsx        |  11 +-
 src/client/views/collections/TreeView.tsx          |  15 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  11 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  25 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |   5 +-
 .../collectionLinear/CollectionLinearView.tsx      |  17 +-
 .../collectionSchema/SchemaTableCell.tsx           |   4 +-
 src/client/views/nodes/ColorBox.tsx                |   5 +-
 src/client/views/nodes/DocumentView.tsx            |  17 +-
 src/client/views/nodes/EquationBox.tsx             |   6 +-
 src/client/views/nodes/ImageBox.tsx                |  11 +-
 src/client/views/nodes/KeyValueBox.tsx             |   2 +-
 src/client/views/nodes/KeyValuePair.tsx            |   7 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  17 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |   5 +-
 src/client/views/nodes/PDFBox.tsx                  |  27 +--
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   3 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  12 +-
 src/client/views/nodes/VideoBox.tsx                |  17 +-
 src/client/views/nodes/WebBox.tsx                  |  25 +--
 src/client/views/nodes/button/FontIconBox.tsx      |  11 +-
 .../views/nodes/formattedText/DashDocView.tsx      |   9 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  29 +--
 src/client/views/nodes/trails/PresElementBox.tsx   |  13 +-
 src/client/views/pdf/PDFViewer.tsx                 |   5 +-
 src/fields/Doc.ts                                  | 228 ++++++++++-----------
 src/fields/DocSymbols.ts                           |  26 +++
 src/fields/FieldSymbols.ts                         |  21 +-
 src/fields/List.ts                                 |  92 ++++-----
 src/fields/util.ts                                 |  80 +++-----
 47 files changed, 496 insertions(+), 463 deletions(-)
 create mode 100644 src/fields/DocSymbols.ts

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 515a870b5..934b9e307 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -2,7 +2,8 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { action, runInAction } from 'mobx';
 import { basename } from 'path';
 import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc';
+import { Doc, DocListCast, Field, Opt, updateCachedAcls } from '../../fields/Doc';
+import { Initializing } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { HtmlField } from '../../fields/HtmlField';
 import { InkField, PointData } from '../../fields/InkField';
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 6c710728b..717473aa1 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -5,7 +5,7 @@ import { Doc, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
 import { listSpec } from '../../fields/Schema';
-import { Cast, CastCtor } from '../../fields/Types';
+import { Cast, CastCtor, DocCast } from '../../fields/Types';
 import { AudioField, ImageField } from '../../fields/URLField';
 import { Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
@@ -328,7 +328,7 @@ export namespace DictationManager {
                 {
                     action: (target: DocumentView) => {
                         const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _layout_autoHeight: true });
-                        const proto = newBox.proto!;
+                        const proto = DocCast(newBox.proto);
                         const prompt = 'Press alt + r to start dictating here...';
                         const head = 3;
                         const anchor = head + prompt.length;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 642ea26da..1f8f4fd45 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -115,8 +115,7 @@ export class DocumentManager {
         });
         if (toReturn.length === 0) {
             DocumentManager.Instance.DocumentViews.forEach(view => {
-                const doc = view.rootDoc.proto;
-                if (doc && doc[Id] && doc[Id] === id) {
+                if (Doc.GetProto(view.rootDoc)?.[Id] === id) {
                     toReturn.push(view);
                 }
             });
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 5e6107e5f..41c2fa889 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,6 +1,7 @@
 import { action, observable, observe } from 'mobx';
 import { computedFn } from 'mobx-utils';
-import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { DirectLinks } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
 import { ProxyField } from '../../fields/Proxy';
 import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
@@ -55,8 +56,8 @@ export class LinkManager {
                             a2 &&
                             Promise.all([Doc.GetProto(a1), Doc.GetProto(a2)]).then(
                                 action(protos => {
-                                    (protos[0] as Doc)?.[DirectLinksSym].add(link);
-                                    (protos[1] as Doc)?.[DirectLinksSym].add(link);
+                                    (protos[0] as Doc)?.[DirectLinks].add(link);
+                                    (protos[1] as Doc)?.[DirectLinks].add(link);
                                 })
                             );
                     });
@@ -69,9 +70,9 @@ export class LinkManager {
             Promise.all([a1, a2]).then(
                 action(() => {
                     if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                        Doc.GetProto(a1)[DirectLinksSym].delete(link);
-                        Doc.GetProto(a2)[DirectLinksSym].delete(link);
-                        Doc.GetProto(link)[DirectLinksSym].delete(link);
+                        Doc.GetProto(a1)[DirectLinks].delete(link);
+                        Doc.GetProto(a2)[DirectLinks].delete(link);
+                        Doc.GetProto(link)[DirectLinks].delete(link);
                     }
                 })
             );
@@ -153,7 +154,7 @@ export class LinkManager {
         return this.relatedLinker(anchor);
     } // finds all links that contain the given anchor
     public getAllDirectLinks(anchor: Doc): Doc[] {
-        return Array.from(Doc.GetProto(anchor)[DirectLinksSym] ?? []);
+        return Array.from(Doc.GetProto(anchor)[DirectLinks] ?? []);
     } // finds all links that contain the given anchor
 
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
@@ -161,7 +162,7 @@ export class LinkManager {
             console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
             return [];
         }
-        const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
+        const dirLinks = Doc.GetProto(anchor)[DirectLinks];
         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());
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 0eecb31cf..97e64ab71 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -5,7 +5,8 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
-import { AclAdmin, AclPrivate, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, HierarchyMapping } from '../../fields/Doc';
+import { Doc, DocListCast, DocListCastAsync, HierarchyMapping } from '../../fields/Doc';
+import { AclAdmin, AclPrivate, DocAcl, AclUnset, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast, StrCast } from '../../fields/Types';
@@ -158,7 +159,7 @@ export class SharingManager extends React.Component<{}> {
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
         return !docs
-            .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
+            .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
             .map(doc => {
                 doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
 
@@ -192,7 +193,7 @@ export class SharingManager extends React.Component<{}> {
 
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
-            .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
+            .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
             .map(doc => {
                 doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
 
@@ -273,7 +274,7 @@ export class SharingManager extends React.Component<{}> {
                 doc.isShared = true;
                 doc.backgroundColor = 'green';
             } else {
-                const acls = doc[DataSym][AclSym];
+                const acls = doc[DocData][DocAcl];
                 if (
                     Object.keys(acls)
                         .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && key !== 'acl-Me')
@@ -485,7 +486,7 @@ export class SharingManager extends React.Component<{}> {
         const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
 
         // handles the case where multiple documents are selected
-        let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]));
+        let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DocData]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DocData]));
 
         if (this.myDocAcls) {
             const newDocs: Doc[] = [];
@@ -493,21 +494,21 @@ export class SharingManager extends React.Component<{}> {
             docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin);
         }
 
-        const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DataSym];
+        const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
 
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
         const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
         const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
+        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
 
         // the list of users shared with
         const userListContents: (JSX.Element | null)[] = users
             .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
                 const userKey = `acl-${normalizeEmail(user.email)}`;
-                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]);
+                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
@@ -533,7 +534,7 @@ export class SharingManager extends React.Component<{}> {
         userListContents.unshift(
             sameAuthor ? (
                 <div key={'owner'} className={'container'}>
-                    <span className={'padding'}>{targetDoc?.author === Doc.CurrentUserEmail ? 'Me' : targetDoc?.author}</span>
+                    <span className="padding">{targetDoc?.author === Doc.CurrentUserEmail ? 'Me' : StrCast(targetDoc?.author)}</span>
                     <div className="edit-actions">
                         <div className={'permissions-dropdown'}>Owner</div>
                     </div>
@@ -555,8 +556,8 @@ export class SharingManager extends React.Component<{}> {
         const groupListContents = groupListMap.map(group => {
             const groupKey = `acl-${StrCast(group.title)}`;
             const uniform = docs
-                .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
-                .every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
+                .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
+                .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
             const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
 
             return !permissions ? null : (
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index ffb2d92f6..6fab4430d 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,7 +1,7 @@
 import { action, computed, observable } from 'mobx';
 import { DateField } from '../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, Opt } from '../../fields/Doc';
-import { InkTool } from '../../fields/InkField';
+import { DocListCast, Opt, Doc } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
 import { Cast, ScriptCast } from '../../fields/Types';
 import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
@@ -36,7 +36,7 @@ export function DocComponent<P extends DocComponentProps>() {
         }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() {
-            return this.props.Document[DataSym] as Doc;
+            return this.props.Document[DocData] as Doc;
         }
         // key where data is stored
         @computed get fieldKey() {
@@ -74,7 +74,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
         }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() {
-            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym];
+            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DocData];
         }
         // key where data is stored
         @computed get fieldKey() {
@@ -118,7 +118,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
         }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() {
-            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym];
+            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DocData];
         }
 
         // key where data is stored
@@ -185,7 +185,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (this.props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
                 return false;
             }
-            const targetDataDoc = this.props.Document[DataSym];
+            const targetDataDoc = this.props.Document[DocData];
             const effectiveAcl = GetEffectiveAcl(targetDataDoc);
 
             if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
@@ -193,7 +193,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                const aclKeys = Object.keys(this.props.Document[AclSym] ?? {});
+                const aclKeys = Object.keys(this.props.Document[DocAcl] ?? {});
                 aclKeys.forEach(key =>
                     added.forEach(d => {
                         if (d.author === denormalizeEmail(key.substring(4)) && !d.createdFrom) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 72c6d449a..e4d50b143 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,7 +6,8 @@ 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 { Doc, DocListCast, Field } from '../../fields/Doc';
+import { AclAdmin, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
@@ -279,8 +280,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 // open centered in a new workspace with Shift Key
                 const embedding = Doc.MakeEmbedding(selectedDocs[0].rootDoc);
                 embedding.embedContainer = undefined;
-                embedding.x = -embedding[WidthSym]() / 2;
-                embedding.y = -embedding[HeightSym]() / 2;
+                embedding.x = -embedding[Width]() / 2;
+                embedding.y = -embedding[Height]() / 2;
                 CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right);
             } else if (e.altKey) {
                 // open same document in new tab
@@ -765,7 +766,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.props.hideDeleteButton ||
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
-                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
+                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);
             });
 
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 025538160..e38474ea0 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -1,7 +1,8 @@
 import * as iink from 'iink-js';
 import { action, observable } from 'mobx';
 import * as React from 'react';
-import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { Height, Width } from '../../fields/DocSymbols';
 import { InkData, InkField, InkTool } from '../../fields/InkField';
 import { Cast, DateCast, NumCast } from '../../fields/Types';
 import { aggregateBounds } from '../../Utils';
@@ -283,8 +284,8 @@ export class InkTranscription extends React.Component {
             action(d => {
                 const x = NumCast(d.x);
                 const y = NumCast(d.y);
-                const width = d[WidthSym]();
-                const height = d[HeightSym]();
+                const width = d[Width]();
+                const height = d[Height]();
                 bounds.push({ x, y, width, height });
             })
         );
@@ -323,8 +324,8 @@ export class InkTranscription extends React.Component {
         // Gets a collection based on the selected nodes using a marquee view ref
         const newCollection = marqViewRef?.getCollection(selected, undefined, true);
         if (newCollection) {
-            newCollection.height = newCollection[HeightSym]();
-            newCollection.width = newCollection[WidthSym]();
+            newCollection.height = newCollection[Height]();
+            newCollection.width = newCollection[Width]();
             // if the grouping we are creating is an individual word
             if (word) {
                 newCollection.title = word;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 545e20ed1..91452fbf5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -23,7 +23,8 @@
 import React = require('react');
 import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, HeightSym, WidthSym } from '../../fields/Doc';
+import { Doc } from '../../fields/Doc';
+import { Height, Width } from '../../fields/DocSymbols';
 import { InkData, InkField } from '../../fields/InkField';
 import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
@@ -370,10 +371,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
 
         // bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be.
-        if (isInkMask && (this.layoutDoc[WidthSym]() !== Math.round(this.layoutDoc[WidthSym]()) || this.layoutDoc[HeightSym]() !== Math.round(this.layoutDoc[HeightSym]()))) {
+        if (isInkMask && (this.layoutDoc[Width]() !== Math.round(this.layoutDoc[Width]()) || this.layoutDoc[Height]() !== Math.round(this.layoutDoc[Height]()))) {
             setTimeout(() => {
-                this.layoutDoc._width = Math.round(NumCast(this.layoutDoc[WidthSym]()));
-                this.layoutDoc._height = Math.round(NumCast(this.layoutDoc[HeightSym]()));
+                this.layoutDoc._width = Math.round(NumCast(this.layoutDoc[Width]()));
+                this.layoutDoc._height = Math.round(NumCast(this.layoutDoc[Height]()));
             });
         }
 
@@ -464,7 +465,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                         style={{
                             color: StrCast(this.layoutDoc.textColor, 'black'),
                             pointerEvents: this.props.isDocumentActive?.() ? 'all' : undefined,
-                            width: this.layoutDoc[WidthSym](),
+                            width: this.layoutDoc[Width](),
                             transform: `scale(${this.props.NativeDimScaling?.() || 1})`,
                             transformOrigin: 'top left',
                             //top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.NativeDimScaling?.() || 1)) / 2,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 50f451c0a..8e112dddb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -10,7 +10,7 @@ import 'normalize.css';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { ScriptField } from '../../fields/ScriptField';
-import { StrCast } from '../../fields/Types';
+import { DocCast, StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
@@ -854,7 +854,7 @@ export class MainView extends React.Component {
         this._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250;
         //setTimeout(action(() => (this._leftMenuFlyoutWidth += 0.5)));
 
-        this._sidebarContent.proto = button.target as any;
+        this._sidebarContent.proto = DocCast(button.target);
         this.LastButton = button;
     });
 
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 00f9aa879..59865cba3 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import ReactLoading from 'react-loading';
-import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { NumCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils';
@@ -177,7 +178,7 @@ export class OverlayView extends React.Component {
                     offsety = 0;
                 const dref = React.createRef<HTMLDivElement>();
                 const onPointerMove = action((e: PointerEvent, down: number[]) => {
-                 if (e.cancelBubble) return false;  // if the overlay doc processed the move event (e.g., to pan its contents), then the event should be marked as canceled since propagation can't be stopped
+                    if (e.cancelBubble) return false; // if the overlay doc processed the move event (e.g., to pan its contents), then the event should be marked as canceled since propagation can't be stopped
                     if (e.buttons === 1) {
                         d.overlayX = e.clientX + offsetx;
                         d.overlayY = e.clientY + offsety;
@@ -214,8 +215,8 @@ export class OverlayView extends React.Component {
                             bringToFront={emptyFunction}
                             addDocument={undefined}
                             removeDocument={this.removeOverlayDoc}
-                            PanelWidth={d[WidthSym]}
-                            PanelHeight={d[HeightSym]}
+                            PanelWidth={d[Width]}
+                            PanelHeight={d[Height]}
                             ScreenToLocalTransform={this.docScreenToLocalXf(d)}
                             renderDepth={1}
                             hideDecorations={true}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5c3ed39a4..1111886d8 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -7,7 +7,8 @@ import { intersection } from 'lodash';
 import { action, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, StrListCast } from '../../fields/Doc';
+import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -69,7 +70,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return this.selectedDoc?.type === DocumentType.LINK;
     }
     @computed get dataDoc() {
-        return this.selectedDoc?.[DataSym];
+        return this.selectedDoc?.[DocData];
     }
 
     @observable layoutFields: boolean = false;
@@ -116,16 +117,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return this.selectedDoc?.type === DocumentType.INK;
     }
 
-    rtfWidth = () => (!this.selectedDoc ? 0 : Math.min(this.selectedDoc?.[WidthSym](), this.props.width - 20));
-    rtfHeight = () => (!this.selectedDoc ? 0 : this.rtfWidth() <= this.selectedDoc?.[WidthSym]() ? Math.min(this.selectedDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT);
+    rtfWidth = () => (!this.selectedDoc ? 0 : Math.min(this.selectedDoc?.[Width](), this.props.width - 20));
+    rtfHeight = () => (!this.selectedDoc ? 0 : this.rtfWidth() <= this.selectedDoc?.[Width]() ? Math.min(this.selectedDoc?.[Height](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT);
 
     @action
     docWidth = () => {
         if (this.selectedDoc) {
             const layoutDoc = this.selectedDoc;
             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;
+            if (aspect) return Math.min(layoutDoc[Width](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.width - 20));
+            return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[Width](), this.props.width - 20) : this.props.width - 20;
         } else {
             return 0;
         }
@@ -205,7 +206,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     setKeyValue = (value: string) => {
-        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
+        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc!] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
         docs.forEach(doc => {
             if (value.indexOf(':') !== -1) {
                 const newVal = value[0].toUpperCase() + value.substring(1, value.length);
@@ -220,7 +221,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const tags = StrListCast(doc.tags);
                 if (!tags.includes(value)) {
                     tags.push(value);
-                    doc[DataSym].tags = tags.length ? new List<string>(tags) : undefined;
+                    doc[DocData].tags = tags.length ? new List<string>(tags) : undefined;
                 }
                 return true;
             }
@@ -301,7 +302,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
-        const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
+        const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc! : DocCast(doc)[DocData]));
         SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
     };
 
@@ -388,7 +389,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get sharingTable() {
         // all selected docs
         const docs =
-            SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
+            SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
 
         const target = docs[0];
 
@@ -397,7 +398,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
+        const commonKeys: string[] = intersection(...docs.map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl]).filter(key => key !== 'acl-Me')));
 
         const tableEntries = [];
 
@@ -405,9 +406,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (commonKeys.length) {
             for (const key of commonKeys) {
                 const name = denormalizeEmail(key.substring(4));
-                const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
+                const uniform = docs.every(doc => doc?.[DocAcl]?.[key] === docs[0]?.[DocAcl]?.[key]);
                 if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
-                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[DocAcl][key])!.name : '-multiple-'));
                 }
             }
         }
@@ -416,7 +417,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
         if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
-        tableEntries.unshift(this.sharingItem('Public', showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
+        tableEntries.unshift(this.sharingItem('Public', showAdmin, StrCast(docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-')));
         tableEntries.unshift(
             this.sharingItem(
                 'Me',
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 6dd76465e..dc430667b 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -14,7 +14,7 @@ import { ObjectField } from '../../../fields/ObjectField';
 import { RichTextField } from '../../../fields/RichTextField';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
@@ -347,7 +347,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
 
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
-        const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : (this.document.proto as Doc);
+        const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : DocCast(this.document.proto);
         //@ts-ignore
         target._type_collection = e.target.selectedOptions[0].value;
     };
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index a65e23911..fc265e2b9 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -2,7 +2,8 @@ import React = require('react');
 import { CursorProperty } from 'csstype';
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, Field, Opt } from '../../../fields/Doc';
+import { DocData, Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
@@ -225,7 +226,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 ref={r => (dref = r || undefined)}
                 Document={doc}
                 pointerEvents={this.blockPointerEventsWhenDragging}
-                DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
+                DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
                 renderDepth={this.props.renderDepth + 1}
                 PanelWidth={width}
                 PanelHeight={height}
@@ -284,7 +285,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         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.layout_fitWidth ? maxWidth : d[WidthSym]();
+        const width = d.layout_fitWidth ? maxWidth : d[Width]();
         return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
     }
 
@@ -294,8 +295,8 @@ 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._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);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Width]() : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Height]() : 0);
         if (nw && nh) {
             const docWid = this.getDocWidth(d);
             return Math.min(maxHeight, (docWid * nh) / nw);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index ea0fbbc54..bbc91aa6e 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,6 +1,7 @@
 import { action, computed, IReactionDisposer, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Height, Width } from '../../../fields/DocSymbols';
 import { NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
@@ -73,12 +74,12 @@ export class CollectionPileView extends CollectionSubView() {
     // toggles the pileup between starburst to compact
     toggleStarburst = action(() => {
         if (this.layoutEngine() === computeStarburstLayout.name) {
-            if (this.rootDoc[WidthSym]() !== NumCast(this.rootDoc._starburstDiameter, 500)) {
-                this.rootDoc._starburstDiameter = this.rootDoc[WidthSym]();
+            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[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2;
-            this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
+            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);
             DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
@@ -87,10 +88,10 @@ export class CollectionPileView extends CollectionSubView() {
             this.props.Document._pileLayoutEngine = computePassLayout.name;
         } else {
             const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500);
-            this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
-            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.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_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/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e2e352857..56f53afe6 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,7 +3,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { CursorProperty } from 'csstype';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, Opt } from '../../../fields/Doc';
+import { DocData, Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
@@ -285,7 +286,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             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;
+            newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
             if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
                 newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
             }
@@ -320,7 +321,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             <DocumentView
                 ref={r => (dref = r || undefined)}
                 Document={doc}
-                DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
+                DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
                 renderDepth={this.props.renderDepth + 1}
                 PanelWidth={width}
                 PanelHeight={height}
@@ -377,7 +378,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const maxWidth = this.columnWidth / this.numGroupColumns;
         if (!this.layoutDoc._columnsFill && !(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d))) {
-            return Math.min(d[WidthSym](), maxWidth);
+            return Math.min(d[Width](), maxWidth);
         }
         return maxWidth;
     }
@@ -386,8 +387,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
-        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[WidthSym]() : 0);
-        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[HeightSym]() : 0);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Width]() : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Height]() : 0);
         if (nw && nh) {
             const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 095e34c39..969658238 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,6 +1,7 @@
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
+import { 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';
@@ -154,15 +155,15 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @action
     remove = (doc: Doc | Doc[]): boolean => {
         const docs = doc instanceof Doc ? [doc] : doc;
-        const targetDataDoc = this.doc[DataSym];
+        const targetDataDoc = this.doc[DocData];
         const value = DocListCast(targetDataDoc[this.props.fieldKey]);
         const result = value.filter(v => !docs.includes(v));
         if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views().some(dv => Doc.AreProtosEqual(dv.rootDoc, doc)))) SelectionManager.DeselectAll();
-        if (result.length !== value.length) {
-            const ind = targetDataDoc[this.props.fieldKey].indexOf(doc);
-            const prev = ind && targetDataDoc[this.props.fieldKey][ind - 1];
+        if (result.length !== value.length && doc instanceof Doc) {
+            const ind = DocListCast(targetDataDoc[this.props.fieldKey]).indexOf(doc);
+            const prev = ind && DocListCast(targetDataDoc[this.props.fieldKey])[ind - 1];
             this.props.removeDocument?.(doc);
-            if (ind > 0) {
+            if (ind > 0 && prev) {
                 FormattedTextBox.SelectOnLoad = prev[Id];
                 DocumentManager.Instance.getDocumentView(prev, this.props.DocumentView?.())?.select(false);
             }
@@ -175,7 +176,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
         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);
+                const res = flg && Doc.AddDocToList(this.doc[DocData], this.props.fieldKey, doc, relativeTo, before);
                 res && (doc.embedContainer = this.props.Document);
                 return res;
             }, true);
@@ -368,8 +369,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     marginX = () => NumCast(this.doc._xMargin);
     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.layout_autoHeightMargins);
+    documentTitleWidth = () => Math.min(this.layoutDoc?.[Width](), this.panelWidth());
+    documentTitleHeight = () => (this.layoutDoc?.[Height]() || 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));
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index b20d05433..710e33b1c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -5,11 +5,11 @@ import { clamp } from 'lodash';
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as ReactDOM from 'react-dom/client';
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, Opt } from '../../../fields/Doc';
+import { DocData, Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
-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';
@@ -292,7 +292,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             PresBox.Instance?.clearSelectedArray();
             pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array
         });
-        if ( // open the presentation trail if it's not already opened
+        if (
+            // open the presentation trail if it's not already opened
             !Array.from(CollectionDockingView.Instance?.tabMap ?? [])
                 .map(d => d.DashDoc)
                 .includes(curPres)
@@ -423,7 +424,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString() : undefined}
                     hideTitle={this.props.keyValue}
                     Document={this._document}
-                    DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
+                    DataDoc={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined}
                     onBrowseClick={MainView.Instance.exploreMode}
                     waitForDoubleClickToClick={MainView.Instance.waitForDoubleClick}
                     isContentActive={returnTrue}
@@ -533,7 +534,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                                 return 'gray';
                         }
                     })(doc.type as DocumentType);
-                    return !background ? undefined : <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: 'absolute', display: 'block', background }} />;
+                    return !background ? undefined : <div style={{ width: doc[Width](), height: doc[Height](), position: 'absolute', display: 'block', background }} />;
             }
         }
     };
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index f5877fa1a..ca9471f6f 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -2,7 +2,8 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
+import { DocData, Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
@@ -146,7 +147,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return NumCast(this.props.treeViewParent.maxEmbedHeight, 200);
     }
     @computed get dataDoc() {
-        return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym];
+        return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DocData];
     }
     @computed get layoutDoc() {
         return Doc.Layout(this.doc);
@@ -191,10 +192,10 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
     @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
         this.props.treeView.props.select(false);
-        const ind = this.dataDoc[key].indexOf(doc);
+        const ind = DocListCast(this.dataDoc[key]).indexOf(doc instanceof Doc ? doc : doc.lastElement());
 
         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.DocumentView?.())?.select(false);
+        res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.props.treeView.props.DocumentView?.())?.select(false);
         return res;
     };
 
@@ -410,7 +411,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     embeddedPanelHeight = () => {
         const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
         return Math.min(
-            layoutDoc[HeightSym](),
+            layoutDoc[Height](),
             this.MAX_EMBED_HEIGHT,
             (() => {
                 const aspect = Doc.NativeAspect(layoutDoc);
@@ -419,7 +420,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     ? !Doc.NativeHeight(layoutDoc)
                         ? NumCast(layoutDoc._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]();
+                    : (this.embeddedPanelWidth() * layoutDoc[Height]()) / layoutDoc[Width]();
             })()
         );
     };
@@ -1190,7 +1191,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 const childLayout = Doc.Layout(pair.layout);
                 const rowHeight = () => {
                     const aspect = Doc.NativeAspect(childLayout);
-                    return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+                    return aspect ? Math.min(childLayout[Width](), rowWidth()) / aspect : childLayout[Height]();
                 };
                 return (
                     <TreeView
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index fee4705e6..1e76d373c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,4 +1,5 @@
-import { Doc, Field, FieldResult, HeightSym, WidthSym } from '../../../../fields/Doc';
+import { Doc, Field, FieldResult } from '../../../../fields/Doc';
+import { Height, Width } from '../../../../fields/DocSymbols';
 import { Id, ToString } from '../../../../fields/FieldSymbols';
 import { ObjectField } from '../../../../fields/ObjectField';
 import { RefField } from '../../../../fields/RefField';
@@ -90,8 +91,8 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
         docMap.set(layout[Id], {
             x: NumCast(layout.x),
             y: NumCast(layout.y),
-            width: layout[WidthSym](),
-            height: layout[HeightSym](),
+            width: layout[Width](),
+            height: layout[Height](),
             pair: { layout, data },
             transition: 'all .3s',
             replica: '',
@@ -105,8 +106,8 @@ export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc
     const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)];
     const burstScale = NumCast(pivotDoc._starburstDocScale, 1);
     childPairs.forEach(({ layout, data }, i) => {
-        const aspect = layout[HeightSym]() / layout[WidthSym]();
-        const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect) * burstScale;
+        const aspect = layout[Height]() / layout[Width]();
+        const docSize = Math.min(Math.min(400, layout[Width]()), Math.min(400, layout[Width]()) / aspect) * burstScale;
         const deg = (i / childPairs.length) * Math.PI * 2;
         docMap.set(layout[Id], {
             x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)),
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8b9698293..811a77fa8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,7 +4,8 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction,
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { DateField } from '../../../../fields/DateField';
-import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+import { DocData, Height, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -707,7 +708,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Rectangle:
                 if (this._inkToTextStartX && this._inkToTextStartY) {
                     const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
-                    const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === 'rtf' && s.color);
+                    const setDocs = this.getActiveDocuments().filter(s => DocCast(s.proto)?.type === DocumentType.RTF && s.color);
                     const sets = setDocs.map(sd => {
                         return Cast(sd.text, RichTextField)?.Text as string;
                     });
@@ -719,9 +720,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     const inks = this.getActiveDocuments().filter(doc => {
                         if (doc.type === 'ink') {
                             const l = NumCast(doc.x);
-                            const r = l + doc[WidthSym]();
+                            const r = l + doc[Width]();
                             const t = NumCast(doc.y);
-                            const b = t + doc[HeightSym]();
+                            const b = t + doc[Height]();
                             const pass = !(this._inkToTextStartX! > r || end[0] < l || this._inkToTextStartY! > b || end[1] < t);
                             return pass;
                         }
@@ -1201,7 +1202,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
         const layoutdoc = Doc.Layout(doc);
         const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
-        const pt2 = xf.transformPoint(NumCast(doc.x) + layoutdoc[WidthSym](), NumCast(doc.y) + layoutdoc[HeightSym]());
+        const pt2 = xf.transformPoint(NumCast(doc.x) + layoutdoc[Width](), NumCast(doc.y) + layoutdoc[Height]());
         const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] };
 
         if (scale !== undefined) {
@@ -1251,7 +1252,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             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;
+            newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
             if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
             else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
             if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
@@ -1291,8 +1292,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 onDoubleClick={this.onChildDoubleClickHandler}
                 onBrowseClick={this.onBrowseClickHandler}
                 ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
-                PanelWidth={childLayout[WidthSym]}
-                PanelHeight={childLayout[HeightSym]}
+                PanelWidth={childLayout[Width]}
+                PanelHeight={childLayout[Height]}
                 docFilters={this.childDocFilters}
                 docRangeFilters={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
@@ -1558,14 +1559,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 this._disposers.groupBounds = reaction(
                     () => {
                         if (this.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
-                            const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
+                            const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[Width](), height: cd[Height]() }));
                             return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
                         }
                         return undefined;
                     },
                     cbounds => {
                         if (cbounds) {
-                            const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
+                            const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[Width]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[Height]() / 2];
                             const p = [NumCast(this.layoutDoc[this.panXFieldKey]), NumCast(this.layoutDoc[this.panYFieldKey])];
                             const pbounds = {
                                 x: cbounds.x - p[0] + c[0],
@@ -1631,8 +1632,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         CollectionFreeFormView.UpdateIcon(
             this.layoutDoc[Id] + '-icon' + new Date().getTime(),
             this.props.docViewPath().lastElement().ContentDiv!,
-            this.layoutDoc[WidthSym](),
-            this.layoutDoc[HeightSym](),
+            this.layoutDoc[Width](),
+            this.layoutDoc[Height](),
             this.props.PanelWidth(),
             this.props.PanelHeight(),
             0,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 641088675..d50c8d2c4 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,6 +1,7 @@
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from '../../../../fields/Doc';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkData, InkField, InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -325,7 +326,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         } else {
             this._downX = x;
             this._downY = y;
-            const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
+            const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
             if ([AclAdmin, AclEdit, AclAugment].includes(effectiveAcl)) {
                 PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge, this.props.slowLoadDocuments);
             }
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 3561844da..ca6b8705d 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -3,9 +3,10 @@ import { Tooltip } from '@material-ui/core';
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { Height, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnTrue, StopEvent, Utils } from '../../../../Utils';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
@@ -40,12 +41,12 @@ export class CollectionLinearView extends CollectionSubView() {
         this._dropDisposer?.();
         this._widthDisposer?.();
         this._selectedDisposer?.();
-        this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
+        this.childLayoutPairs.map((pair, ind) => ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
     }
 
     componentDidMount() {
         this._widthDisposer = reaction(
-            () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[WidthSym]() || this.dimension()) + tot + 4, 0) : 0),
+            () => 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),
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
@@ -61,11 +62,11 @@ export class CollectionLinearView extends CollectionSubView() {
                         if (isSelected) {
                             selected = pair;
                         } else {
-                            ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+                            ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
                         }
                     });
                     if (selected && selected.layout) {
-                        ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
+                        ScriptCast(DocCast(selected.layout.proto)?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
                     }
                 }),
             { fireImmediately: true }
@@ -204,8 +205,8 @@ export class CollectionLinearView extends CollectionSubView() {
                     rootSelected={this.props.isSelected}
                     removeDocument={this.props.removeDocument}
                     ScreenToLocalTransform={docXf}
-                    PanelWidth={doc[WidthSym]}
-                    PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
+                    PanelWidth={doc[Width]}
+                    PanelHeight={nested || doc._height ? doc[Height] : this.dimension}
                     renderDepth={this.props.renderDepth + 1}
                     dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
                     focus={emptyFunction}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 01bfbcddb..876693474 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -6,7 +6,7 @@ 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 { BoolCast, Cast, DateCast, DocCast, FieldValue } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
 import { FInfo } from '../../../documents/Documents';
@@ -58,7 +58,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
                 break;
             }
             protoCount++;
-            doc = doc.proto;
+            doc = DocCast(doc.proto);
         }
         const parenCount = Math.max(0, protoCount - 1);
         const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue';
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 330cc3971..aae759702 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -2,7 +2,8 @@ import React = require('react');
 import { action } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { Doc } from '../../../fields/Doc';
+import { Height, Width } from '../../../fields/DocSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -48,7 +49,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     render() {
-        const scaling = Math.min(this.layoutDoc.layout_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[Height](), this.props.PanelWidth() / this.rootDoc[Width]());
         return (
             <div
                 className={`colorBox-container${this.props.isContentActive() ? '-interactive' : ''}`}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cc105326f..1c7ef5217 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -3,7 +3,8 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
-import { AclPrivate, AnimationSym, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
+import { AclPrivate, Animation, DocData, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
@@ -794,7 +795,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
         !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' });
         !Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.props.Document), icon: 'hand-point-right' });
-        !Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.props.Document[DataSym]), icon: 'hand-point-right' });
+        !Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.props.Document[DocData]), icon: 'hand-point-right' });
 
         let documentationDescription: string | undefined = undefined;
         let documentationLink: string | undefined = undefined;
@@ -1146,7 +1147,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     renderDoc = (style: object) => {
         TraceMobx();
-        return !DocCast(this.Document) || GetEffectiveAcl(this.Document[DataSym]) === AclPrivate
+        return !DocCast(this.Document) || GetEffectiveAcl(this.Document[DocData]) === AclPrivate
             ? null
             : this.docContents ?? (
                   <div
@@ -1231,7 +1232,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents,
                 }}>
                 <>
-                    {DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[AnimationSym], this.rootDoc)}
+                    {DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)}
                     {borderPath?.jsx}
                 </>
             </div>
@@ -1265,8 +1266,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     public setAnimEffect = (presEffect: Doc, timeInMs: number, afterTrans?: () => void) => {
         this.AnimEffectTimer && clearTimeout(this.AnimEffectTimer);
-        this.rootDoc[AnimationSym] = presEffect;
-        this.AnimEffectTimer = setTimeout(() => (this.rootDoc[AnimationSym] = undefined), timeInMs);
+        this.rootDoc[Animation] = presEffect;
+        this.AnimEffectTimer = setTimeout(() => (this.rootDoc[Animation] = undefined), timeInMs);
     };
     public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => {
         this.rootDoc._viewTransition = `${transProp} ${timeInMs}ms`;
@@ -1579,7 +1580,7 @@ ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuff
 ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
     const linkSource = Cast(linkCollection.linkSource, Doc, null);
     const collectedLinks = DocListCast(Doc.GetProto(linkCollection).data);
-    let wid = linkSource[WidthSym]();
+    let wid = linkSource[Width]();
     const links = LinkManager.Links(linkSource);
     links.forEach(link => {
         const other = LinkManager.getOppositeAnchor(link, linkSource);
@@ -1589,7 +1590,7 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
             embedding.x = wid;
             embedding.y = 0;
             embedding._lockedPosition = false;
-            wid += otherdoc[WidthSym]();
+            wid += otherdoc[Width]();
             Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', embedding);
         }
     });
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index f17ab06e7..8c6eb4532 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -2,7 +2,7 @@ import EquationEditor from 'equation-editor-react';
 import { action, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { WidthSym } from '../../../fields/Doc';
+import { Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
@@ -67,7 +67,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (e.key === 'Tab') {
             const graph = Docs.Create.FunctionPlotDocument([this.rootDoc], {
-                x: NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym](),
+                x: NumCast(this.layoutDoc.x) + this.layoutDoc[Width](),
                 y: NumCast(this.layoutDoc.y),
                 _width: 400,
                 _height: 300,
@@ -104,7 +104,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     fontSize: StrCast(this.rootDoc._fontSize),
                 }}
                 onKeyDown={e => e.stopPropagation()}>
-                <EquationEditor ref={this._ref} value={this.dataDoc.text || 'x'} spaceBehavesLikeTab={true} onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
+                <EquationEditor ref={this._ref} value={StrCast(this.dataDoc.text, 'x')} spaceBehavesLikeTab={true} onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
             </div>
         );
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 068d39391..5b302e7ce 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -3,7 +3,8 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { extname } from 'path';
-import { DataSym, Doc, DocListCast, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { DocData, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
@@ -106,7 +107,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         );
         const layoutDoc = this.layoutDoc;
         this._disposers.path = reaction(
-            () => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
+            () => ({ nativeSize: this.nativeSize, width: this.layoutDoc[Width]() }),
             ({ nativeSize, width }) => {
                 if (layoutDoc === this.layoutDoc || !this.layoutDoc._height) {
                     this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
@@ -150,7 +151,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                 const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
                 const targetField = Doc.LayoutFieldKey(layoutDoc);
-                const targetDoc = layoutDoc[DataSym];
+                const targetDoc = layoutDoc[DocData];
                 if (targetDoc[targetField] instanceof ImageField) {
                     this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
                     Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey);
@@ -230,7 +231,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         croppingProto.freeform_panYMax = anchh / viewScale;
         if (addCrop) {
             DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
-            cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]();
+            cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[Width]();
             cropping.y = NumCast(this.rootDoc.y);
             this.props.addDocTab(cropping, OpenWhere.inParent);
         }
@@ -302,7 +303,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     considerGooglePhotosLink = () => {
-        const remoteUrl = this.dataDoc.googlePhotosUrl;
+        const remoteUrl = StrCast(this.dataDoc.googlePhotosUrl); // bcz: StrCast or URLCast???
         return !remoteUrl ? null : <img draggable={false} style={{ transformOrigin: 'bottom right' }} id={'google-photos'} src={'/assets/google_photos.png'} onClick={() => window.open(remoteUrl)} />;
     };
 
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 88a82e8e6..5b6b0b5a7 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -84,7 +84,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean {
         const { script, type, onDelegate } = kvpScript;
         //const target = onDelegate ? Doc.Layout(doc.layout) : Doc.GetProto(doc); // bcz: TODO need to be able to set fields on layout templates
-        const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : doc.proto || doc;
+        const target = forceOnDelegate || onDelegate || key.startsWith('_') ? doc : DocCast(doc.proto, doc);
         let field: Field;
         if (type === 'computed') {
             field = new ComputedField(script);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 91df928c4..4e348c67c 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -13,6 +13,7 @@ import { KeyValueBox } from './KeyValueBox';
 import './KeyValueBox.scss';
 import './KeyValuePair.scss';
 import React = require('react');
+import { DocCast } from '../../../fields/Types';
 
 // Represents one row in a key value plane
 
@@ -87,7 +88,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                 break;
             }
             protoCount++;
-            doc = doc.proto;
+            doc = DocCast(doc.proto);
         }
         const parenCount = Math.max(0, protoCount - 1);
         const keyStyle = protoCount === 0 ? 'black' : 'blue';
@@ -104,12 +105,12 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                             onClick={undoBatch(() => {
                                 if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
                                     delete props.Document[props.fieldKey];
-                                } else delete props.Document.proto![props.fieldKey];
+                                } else delete DocCast(props.Document.proto)?.[props.fieldKey];
                             })}>
                             X
                         </button>
                         <input className="keyValuePair-td-key-check" type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
-                        <div className='keyValuePair-keyField' style={{ marginLeft: 35 * (props.fieldKey.match(/_/g)?.length ||0), color: keyStyle }}>
+                        <div className="keyValuePair-keyField" style={{ marginLeft: 35 * (props.fieldKey.match(/_/g)?.length || 0), color: keyStyle }}>
                             {'('.repeat(parenCount)}
                             {props.fieldKey}
                             {')'.repeat(parenCount)}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 450176a49..079e1b56d 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,11 +3,12 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import wiki from 'wikijs';
-import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, DocCastAsync, Opt } from '../../../fields/Doc';
+import { Height, Width } from '../../../fields/DocSymbols';
 import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 import { DocServer } from '../../DocServer';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
@@ -183,17 +184,17 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
 
     width = () => {
         if (!this._targetDoc) return 225;
-        if (this._targetDoc[WidthSym]() < this._targetDoc?.[HeightSym]()) {
-            return (Math.min(225, this._targetDoc[HeightSym]()) * this._targetDoc[WidthSym]()) / this._targetDoc[HeightSym]();
+        if (this._targetDoc[Width]() < this._targetDoc?.[Height]()) {
+            return (Math.min(225, this._targetDoc[Height]()) * this._targetDoc[Width]()) / this._targetDoc[Height]();
         }
-        return Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
+        return Math.min(225, NumCast(this._targetDoc?.[Width](), 225));
     };
     height = () => {
         if (!this._targetDoc) return 225;
-        if (this._targetDoc[WidthSym]() > this._targetDoc?.[HeightSym]()) {
-            return (Math.min(225, this._targetDoc[WidthSym]()) * this._targetDoc[HeightSym]()) / this._targetDoc[WidthSym]();
+        if (this._targetDoc[Width]() > this._targetDoc?.[Height]()) {
+            return (Math.min(225, this._targetDoc[Width]()) * this._targetDoc[Height]()) / this._targetDoc[Width]();
         }
-        return Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
+        return Math.min(225, NumCast(this._targetDoc?.[Height](), 225));
     };
     @computed get previewHeader() {
         return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : (
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 9354f9639..18fcc951a 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -3,7 +3,8 @@ import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-m
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+import { Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { NumCast, StrCast } from '../../../../fields/Types';
@@ -367,7 +368,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         .ScreenToLocalTransform()
                         .scale(this.props.NativeDimScaling?.() || 1)
                         .transformDirection(delta[0], delta[1]);
-                    const fullWidth = this.layoutDoc[WidthSym]();
+                    const fullWidth = this.layoutDoc[Width]();
                     const mapWidth = fullWidth - this.sidebarWidth();
                     if (this.sidebarWidth() + localDelta[0] > 0) {
                         this._showSidebar = true;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 95fbb274d..6ee1b67f2 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,7 +3,8 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from 'mobx-react';
 import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { ComputedField } from '../../../fields/ScriptField';
@@ -63,7 +64,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._layout_fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        !this.Document._layout_fitWidth && (this.Document._height = this.Document[Width]() * (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)));
@@ -104,15 +105,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = this.layoutDoc[WidthSym]().toString();
-        newDiv.style.height = this.layoutDoc[HeightSym]().toString();
+        newDiv.style.width = this.layoutDoc[Width]().toString();
+        newDiv.style.height = this.layoutDoc[Height]().toString();
         this.replaceCanvases(docViewContent, newDiv);
         const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv);
 
         const anchx = NumCast(cropping.x);
         const anchy = NumCast(cropping.y);
-        const anchw = cropping[WidthSym]() * (this.props.NativeDimScaling?.() || 1);
-        const anchh = cropping[HeightSym]() * (this.props.NativeDimScaling?.() || 1);
+        const anchw = cropping[Width]() * (this.props.NativeDimScaling?.() || 1);
+        const anchh = cropping[Height]() * (this.props.NativeDimScaling?.() || 1);
         const viewScale = 1;
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
@@ -169,8 +170,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             CollectionFreeFormView.UpdateIcon(
                 filename,
                 docViewContent,
-                this.layoutDoc[WidthSym](),
-                this.layoutDoc[HeightSym](),
+                this.layoutDoc[Width](),
+                this.layoutDoc[Height](),
                 this.props.PanelWidth(),
                 this.props.PanelHeight(),
                 NumCast(this.layoutDoc._layout_scrollTop),
@@ -260,8 +261,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         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._layout_fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        this.layoutDoc._height = this.layoutDoc[Width]() / (Doc.NativeAspect(this.dataDoc) || 1);
+        !this.Document._layout_fitWidth && (this.Document._height = this.Document[Width]() * (nh / nw));
     };
 
     public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => {
@@ -336,7 +337,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 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]);
+                    onButton && (this.layoutDoc._width = this.layoutDoc[Width]() + localDelta[0]);
                     this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
                 }
                 return false;
@@ -357,11 +358,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
             this._previewNativeWidth = nativeWidth * sideratio;
-            this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
+            this._previewWidth = (this.layoutDoc[Width]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
         } else {
             this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
-            this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
+            this.layoutDoc._width = (this.layoutDoc[Width]() * nativeWidth * pdfratio) / curNativeWidth;
             this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
         }
     });
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 80b12b96e..04f11a5df 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -11,6 +11,7 @@ import { DocumentType } from '../../../documents/DocumentTypes';
 import { Presentation } from '../../../util/TrackMovements';
 import { Doc } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
+import { DocCast } from '../../../../fields/Types';
 
 @observer
 export class RecordingBox extends ViewBoxBaseComponent() {
@@ -57,7 +58,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
     render() {
         return (
             <div className="recordingBox" ref={this._ref}>
-                {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={this.rootDoc.proto?.[Id] || ''} />}
+                {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={DocCast(this.rootDoc.proto)?.[Id] || ''} />}
             </div>
         );
     }
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 7bf765042..1e178b123 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -5,10 +5,11 @@ import { computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
 import { DateField } from '../../../fields/DateField';
-import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { Doc } from '../../../fields/Doc';
+import { Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast } from '../../../fields/Types';
+import { Cast, DocCast, NumCast } from '../../../fields/Types';
 import { AudioField, VideoField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
@@ -24,6 +25,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import './ScreenshotBox.scss';
 import { VideoBox } from './VideoBox';
+
 declare class MediaRecorder {
     constructor(e: any, options?: any); // whatever MediaRecorder has
 }
@@ -140,7 +142,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         if (!nativeWidth || !nativeHeight) {
             if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
             Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
-            this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
+            this.layoutDoc._height = (this.layoutDoc[Width]() || 0) / aspect;
         }
     };
 
@@ -277,7 +279,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         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[Height]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[Width]())) * this.props.PanelWidth();
     formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
     render() {
         TraceMobx();
@@ -315,7 +317,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                         {!(this.dataDoc[this.fieldKey + '-dictation'] instanceof Doc) ? null : (
                             <FormattedTextBox
                                 {...this.props}
-                                Document={this.dataDoc[this.fieldKey + '-dictation']}
+                                Document={DocCast(this.dataDoc[this.fieldKey + '-dictation'])}
                                 fieldKey={'text'}
                                 PanelHeight={this.formattedPanelHeight}
                                 select={emptyFunction}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9cf929679..34dcde23b 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -3,7 +3,8 @@ 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, StrListCast, WidthSym } from '../../../fields/Doc';
+import { Doc, StrListCast } from '../../../fields/Doc';
+import { Height, Width } from '../../../fields/DocSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
@@ -14,6 +15,7 @@ import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
 import { DocumentManager } from '../../util/DocumentManager';
+import { FollowLinkScript } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { ReplayMovements } from '../../util/ReplayMovements';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -33,8 +35,6 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { RecordingBox } from './RecordingBox';
 import { PinProps, PresBox } from './trails';
 import './VideoBox.scss';
-import { ScriptField } from '../../../fields/ScriptField';
-import { FollowLinkScript } from '../../util/LinkFollower';
 const path = require('path');
 
 /**
@@ -111,7 +111,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     // returns the presentation data if it exists, null otherwise
     @computed get presentation() {
         const data = this.dataDoc[this.fieldKey + '-presentation'];
-        return data ? JSON.parse(data) : null;
+        return data ? JSON.parse(StrCast(data)) : null;
     }
 
     @computed private get timeline() {
@@ -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[Width]();
+            this.dataDoc.icon_nativeHeight = this.layoutDoc[Height]();
         };
         this.Snapshot(undefined, undefined, makeIcon);
     };
@@ -465,7 +465,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 { fireImmediately: true }
             );
 
-            (!this.dataDoc[this.fieldKey + '_thumbnails'] || this.dataDoc[this.fieldKey + '_thumbnails'].length != VideoBox.numThumbnails) && this.getVideoThumbnails();
+            (!this.dataDoc[this.fieldKey + '_thumbnails'] || StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']).length != VideoBox.numThumbnails) && this.getVideoThumbnails();
         }
     };
 
@@ -870,7 +870,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 }),
                 returnFalse,
                 () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
-                false, false
+                false,
+                false
             );
         }
     };
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a898398a1..48f77469f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -2,16 +2,18 @@ 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, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
+import { Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { HtmlField } from '../../../fields/HtmlField';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
+import { RefField } from '../../../fields/RefField';
 import { listSpec } from '../../../fields/Schema';
 import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, removeStyleSheetRule, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
@@ -37,7 +39,6 @@ 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');
@@ -84,7 +85,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._url ? WebBox.urlHash(this._url) + '' : '';
     }
     @computed get scrollHeight() {
-        return Math.max(this.layoutDoc[HeightSym](), this._scrollHeight);
+        return Math.max(this.layoutDoc[Height](), this._scrollHeight);
     }
     @computed get allAnnotations() {
         return DocListCast(this.dataDoc[this.annotationKey]);
@@ -214,7 +215,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
                     if (!nativeWidth) Doc.SetNativeWidth(this.layoutDoc, 600);
                     Doc.SetNativeHeight(this.layoutDoc, (nativeWidth || 600) / youtubeaspect);
-                    this.layoutDoc._height = this.layoutDoc[WidthSym]() / youtubeaspect;
+                    this.layoutDoc._height = this.layoutDoc[Width]() / youtubeaspect;
                 }
             } // else it's an HTMLfield
         } else if (this.webField && !this.dataDoc.text) {
@@ -281,7 +282,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._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight));
+            const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[Height](), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[Height](), this._scrollHeight));
             if (scrollTo !== undefined) {
                 if (this._initialScroll === undefined) {
                     const focusTime = options.zoomTime ?? 500;
@@ -460,7 +461,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._scrollHeight = Math.max(this._scrollHeight, (iframeContent.body.children[0] as any)?.scrollHeight || 0);
                 if (this._scrollHeight) {
                     this.rootDoc.nativeHeight = Math.min(NumCast(this.rootDoc.nativeHeight), this._scrollHeight);
-                    this.layoutDoc.height = Math.min(this.layoutDoc[HeightSym](), (this.layoutDoc[WidthSym]() * NumCast(this.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth));
+                    this.layoutDoc.height = Math.min(this.layoutDoc[Height](), (this.layoutDoc[Width]() * NumCast(this.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth));
                 }
             };
             initHeights();
@@ -784,7 +785,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
-                    onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
+                    onButton && (this.layoutDoc._width = this.layoutDoc[Width]() + localDelta[0]);
                     this.layoutDoc._layout_showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
                 }
                 return false;
@@ -820,9 +821,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     toggleSidebar = action((preview: boolean = false) => {
         var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         if (!nativeWidth) {
-            const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym]();
+            const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[Width]();
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
-            Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth);
+            Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[Height]() / this.Document[Width]()) * defaultNativeWidth);
             nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         }
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
@@ -830,11 +831,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
             this._previewNativeWidth = nativeWidth * sideratio;
-            this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
+            this._previewWidth = (this.layoutDoc[Width]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
         } else {
             this.layoutDoc._layout_showSidebar = !this.layoutDoc._layout_showSidebar;
-            this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
+            this.layoutDoc._width = (this.layoutDoc[Width]() * nativeWidth * pdfratio) / curNativeWidth;
             if (!this.layoutDoc._layout_showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
                 this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '_nativeWidth'] = undefined;
             } else {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index fb29f95f4..7f08de246 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,7 +5,8 @@ import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
-import { Doc, HeightSym, StrListCast, WidthSym } from '../../../../fields/Doc';
+import { Doc, StrListCast } from '../../../../fields/Doc';
+import { Height, Width } from '../../../../fields/DocSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -757,8 +758,8 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
                 action(d => {
                     const x = NumCast(d.x);
                     const y = NumCast(d.y);
-                    const width = d[WidthSym]();
-                    const height = d[HeightSym]();
+                    const width = d[Width]();
+                    const height = d[Height]();
                     bounds.push({ x, y, width, height });
                 })
             );
@@ -795,8 +796,8 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
             // TODO: nda - this is the code to actually get a new grouped collection
             const newCollection = marqViewRef?.getCollection(selected, undefined, true);
             if (newCollection) {
-                newCollection.height = newCollection[HeightSym]();
-                newCollection.width = newCollection[WidthSym]();
+                newCollection.height = newCollection[Height]();
+                newCollection.width = newCollection[Width]();
             }
 
             // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index c929b7ff3..1d41eb379 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -2,8 +2,9 @@ import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { NodeSelection } from 'prosemirror-state';
 import * as ReactDOM from 'react-dom/client';
-import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Doc } from '../../../../fields/Doc';
+import { Height, Width } from '../../../../fields/DocSymbols';
+import { NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
@@ -210,8 +211,8 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     addDocTab={this._textBox.props.addDocTab}
                     pinToPres={returnFalse}
                     renderDepth={this._textBox.props.renderDepth + 1}
-                    PanelWidth={this._finalLayout[WidthSym]}
-                    PanelHeight={this._finalLayout[HeightSym]}
+                    PanelWidth={this._finalLayout[Width]}
+                    PanelHeight={this._finalLayout[Height]}
                     focus={this.outerFocus}
                     whenChildContentsActiveChanged={returnFalse}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fb0c0d2ab..bea789a5c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,9 +12,11 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, StrListCast, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, Height, UpdatingFromServer, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
 import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
@@ -34,6 +36,7 @@ import { DragManager } from '../../../util/DragManager';
 import { MakeTemplate } from '../../../util/DropConverter';
 import { IsFollowLinkScript } from '../../../util/LinkFollower';
 import { LinkManager } from '../../../util/LinkManager';
+import { RTFMarkup } from '../../../util/RTFMarkup';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -68,8 +71,6 @@ import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
 import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
-import { RTFMarkup } from '../../../util/RTFMarkup';
-import { List } from '../../../../fields/List';
 const translateGoogleApi = require('translate-google-api');
 export const GoogleRef = 'googleDocId';
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -544,8 +545,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             } else if (de.embedKey) {
                 const target = dragData.droppedDocuments[0];
                 const node = schema.nodes.dashDoc.create({
-                    width: target[WidthSym](),
-                    height: target[HeightSym](),
+                    width: target[Width](),
+                    height: target[Height](),
                     title: 'dashDoc',
                     docId: target[Id],
                     float: 'unset',
@@ -646,7 +647,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const hr = Math.round(Date.now() / 1000 / 60 / 60);
             numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
         }
-        this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css changes happen outside of react/mobx.  so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView)
+        this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css changes happen outside of react/mobx.  so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView)
     };
 
     @observable _showSidebar = false;
@@ -682,7 +683,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             .scale(this.props.NativeDimScaling?.() || 1)
             .transformDirection(delta[0], delta[1]);
         const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
-        const width = this.layoutDoc[WidthSym]() + localDelta[0];
+        const width = this.layoutDoc[Width]() + localDelta[0];
         this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
         this.layoutDoc.width = width;
         this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
@@ -1163,7 +1164,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             () => {
                 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;
-                return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(whichDoc[this.fieldKey]) };
+                return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(DocCast(whichDoc[this.fieldKey])) };
             },
             incomingValue => {
                 if (this._editorView && this._applyingChange !== this.fieldKey) {
@@ -1208,7 +1209,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             () => this.props.isSelected(),
             action(selected => {
                 if (FormattedTextBox._globalHighlights.has('Bold Text')) {
-                    this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
+                    this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
                 }
                 if (RichTextMenu.Instance?.view === this._editorView && !selected) {
                     RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
@@ -2000,14 +2001,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `_${usePath.replace(':hover', '')}` : '');
     }
     @observable _isHovering = false;
-    onPassiveWheel = (e:WheelEvent) => { 
+    onPassiveWheel = (e: WheelEvent) => {
         // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
         if (this.props.isContentActive() && !this.props.allowScroll) {
             if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault();
             e.stopPropagation();
         }
-    }
-    _oldWheel:any;
+    };
+    _oldWheel: any;
     render() {
         TraceMobx();
         const active = this.props.isContentActive() || this.props.isSelected();
@@ -2029,8 +2030,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 onPointerLeave={action(() => (this._isHovering = false))}
                 ref={r => {
                     this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
-                    this._oldWheel= r;
-                    r?.addEventListener('wheel', this.onPassiveWheel,  { passive: false } );
+                    this._oldWheel = r;
+                    r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
                 }}
                 style={{
                     ...(this.props.dontScale
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f197a8a8d..154c879a0 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -2,12 +2,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+import { Height, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-import { Docs, DocUtils } from '../../../documents/Documents';
+import { Docs } from '../../../documents/Documents';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
@@ -322,7 +323,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     removeAllRecordingInOverlay = () => {
-        DocListCast(Doc.MyOverlayDocs.data).filter(doc => doc.slides === this.rootDoc).forEach(Doc.RemFromMyOverlay);
+        DocListCast(Doc.MyOverlayDocs.data)
+            .filter(doc => doc.slides === this.rootDoc)
+            .forEach(Doc.RemFromMyOverlay);
     };
 
     static removeEveryExistingRecordingInOverlay = () => {
@@ -386,8 +389,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             activeItem.recording = recording;
 
             // make recording box appear in the bottom right corner of the screen
-            recording.overlayX = window.innerWidth - recording[WidthSym]() - 20;
-            recording.overlayY = window.innerHeight - recording[HeightSym]() - 20;
+            recording.overlayX = window.innerWidth - recording[Width]() - 20;
+            recording.overlayY = window.innerHeight - recording[Height]() - 20;
             Doc.AddToMyOverlay(recording);
         }
     };
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 999e3eeb6..bc0cad636 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,7 +2,8 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
 import { observer } from 'mobx-react';
 import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
-import { Doc, DocListCast, Field, HeightSym, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
+import { Height } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
@@ -181,7 +182,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
+            const scrollTo = Utils.scrollIntoView(scrollTop, doc[Height](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._layout_scrollTop) {
                 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);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index d0e5d03b3..72eeedcff 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -11,9 +11,38 @@ import { SelectionManager } from '../client/util/SelectionManager';
 import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
 import { undoable, UndoManager } from '../client/util/UndoManager';
 import { decycle } from '../decycler/decycler';
+import * as JSZipUtils from '../JSZipUtils';
 import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils';
 import { DateField } from './DateField';
-import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols';
+import {
+    AclAdmin,
+    AclAugment,
+    AclEdit,
+    AclPrivate,
+    AclReadonly,
+    AclSelfEdit,
+    AclUnset,
+    Animation,
+    CachedUpdates,
+    DirectLinks,
+    DocAcl,
+    DocCss,
+    DocData,
+    DocFields,
+    DocLayout,
+    FieldKeys,
+    FieldTuples,
+    ForceServerWrite,
+    Height,
+    Highlight,
+    Initializing,
+    Self,
+    SelfProxy,
+    Update,
+    UpdatingFromServer,
+    Width,
+} from './DocSymbols';
+import { Copy, HandleUpdate, Id, OnUpdate, Parent, ToScriptString, ToString } from './FieldSymbols';
 import { InkField, InkTool } from './InkField';
 import { List, ListFieldName } from './List';
 import { ObjectField } from './ObjectField';
@@ -26,7 +55,6 @@ import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Ty
 import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
 import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from './util';
 import JSZip = require('jszip');
-import * as JSZipUtils from '../JSZipUtils';
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
         const onDelegate = Object.keys(doc).includes(key.replace(/^_/, ''));
@@ -93,28 +121,6 @@ export function DocListCast(field: FieldResult, defaultVal: Doc[] = []) {
     return Cast(field, listSpec(Doc), defaultVal).filter(d => d instanceof Doc) as Doc[];
 }
 
-export const WidthSym = Symbol('Width');
-export const HeightSym = Symbol('Height');
-export const AnimationSym = Symbol('Animation');
-export const HighlightSym = Symbol('Highlight');
-export const DataSym = Symbol('Data');
-export const LayoutSym = Symbol('Layout');
-export const FieldsSym = Symbol('Fields');
-export const CssSym = Symbol('Css');
-export const AclSym = Symbol('Acl');
-export const DirectLinksSym = Symbol('DirectLinks');
-export const AclUnset = Symbol('AclUnset');
-export const AclPrivate = Symbol('AclOwnerOnly');
-export const AclReadonly = Symbol('AclReadOnly');
-export const AclAugment = Symbol('AclAugment');
-export const AclSelfEdit = Symbol('AclSelfEdit');
-export const AclEdit = Symbol('AclEdit');
-export const AclAdmin = Symbol('AclAdmin');
-export const UpdatingFromServer = Symbol('UpdatingFromServer');
-export const Initializing = Symbol('Initializing');
-export const ForceServerWrite = Symbol('ForceServerWrite');
-export const CachedUpdates = Symbol('Cached updates');
-
 export enum aclLevel {
     unset = -1,
     unshared = 0,
@@ -141,15 +147,15 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }>
 export function updateCachedAcls(doc: Doc) {
     if (!doc) return;
 
-    const target = (doc as any)?.__fields ?? doc;
+    const target = (doc as any)?.__fieldTuples ?? doc;
     const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {};
     Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl));
-    if (Object.keys(permissions).length || doc[AclSym]?.length) {
-        runInAction(() => (doc[AclSym] = permissions));
+    if (Object.keys(permissions).length || doc[DocAcl]?.length) {
+        runInAction(() => (doc[DocAcl] = permissions));
     }
 
     if (doc.proto instanceof Promise) {
-        doc.proto.then(updateCachedAcls);
+        doc.proto.then(proto => updateCachedAcls(DocCast(proto)));
         return doc.proto;
     }
 }
@@ -295,25 +301,20 @@ export class Doc extends RefField {
             set: setter,
             get: getter,
             // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter
-            has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fields,
+            has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fieldTuples,
             ownKeys: target => {
-                const obj = {} as any;
-                if (GetEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fieldKeys);
-                runInAction(() => (obj.__LAYOUT__ = target.__LAYOUT__));
-                return Object.keys(obj);
+                const keys = GetEffectiveAcl(target) !== AclPrivate ? Object.keys(target[FieldKeys]) : [];
+                return [...keys, '__LAYOUT__'];
             },
             getOwnPropertyDescriptor: (target, prop) => {
-                if (prop.toString() === '__LAYOUT__') {
+                if (prop.toString() === '__LAYOUT__' || !(prop in target[FieldKeys])) {
                     return Reflect.getOwnPropertyDescriptor(target, prop);
                 }
-                if (prop in target.__fieldKeys) {
-                    return {
-                        configurable: true, //TODO Should configurable be true?
-                        enumerable: true,
-                        value: 0, //() => target.__fields[prop])
-                    };
-                }
-                return Reflect.getOwnPropertyDescriptor(target, prop);
+                return {
+                    configurable: true, //TODO Should configurable be true?
+                    enumerable: true,
+                    value: 0, //() => target.__fieldTuples[prop])
+                };
             },
             deleteProperty: deleteProperty,
             defineProperty: () => {
@@ -327,41 +328,35 @@ export class Doc extends RefField {
         return docProxy;
     }
 
-    proto: Opt<Doc>;
     [key: string]: FieldResult;
 
     @serializable(alias('fields', map(autoObject(), { afterDeserialize: afterDocDeserialize })))
-    private get __fields() {
-        return this.___fields;
+    private get __fieldTuples() {
+        return this[FieldTuples];
     }
-    private set __fields(value) {
-        this.___fields = value;
+    private set __fieldTuples(value) {
+        // called by deserializer to set all fields in one shot
+        this[FieldTuples] = value;
         for (const key in value) {
             const field = value[key];
-            field !== undefined && (this.__fieldKeys[key] = true);
+            field !== undefined && (this[FieldKeys][key] = true);
             if (!(field instanceof ObjectField)) continue;
             field[Parent] = this[Self];
             field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
         }
     }
-    private get __fieldKeys() {
-        return this.___fieldKeys;
-    }
-    private set __fieldKeys(value) {
-        this.___fieldKeys = value;
-    }
 
-    @observable private ___fields: any = {};
-    @observable private ___fieldKeys: any = {};
+    @observable private [FieldTuples]: any = {};
+    @observable private [FieldKeys]: any = {};
     /// all of the raw acl's that have been set on this document.  Use GetEffectiveAcl to determine the actual ACL of the doc for editing
-    @observable public [AclSym]: { [key: string]: symbol } = {};
-    @observable public [CssSym]: number = 0; // incrementer denoting a change to CSS layout
-    @observable public [DirectLinksSym]: Set<Doc> = new Set();
-    @observable public [AnimationSym]: Opt<Doc>;
-    @observable public [HighlightSym]: boolean = false;
+    @observable public [DocAcl]: { [key: string]: symbol } = {};
+    @observable public [DocCss]: number = 0; // incrementer denoting a change to CSS layout
+    @observable public [DirectLinks]: Set<Doc> = new Set();
+    @observable public [Animation]: Opt<Doc>;
+    @observable public [Highlight]: boolean = false;
     static __Anim(Doc: Doc) {
         // for debugging to print AnimationSym field easily.
-        return Doc[AnimationSym];
+        return Doc[Animation];
     }
 
     private [UpdatingFromServer]: boolean = false;
@@ -374,15 +369,15 @@ export class Doc extends RefField {
 
     private [Self] = this;
     private [SelfProxy]: any;
-    public [FieldsSym] = () => this[Self].___fields; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any);
-    public [WidthSym] = () => NumCast(this[SelfProxy]._width);
-    public [HeightSym] = () => NumCast(this[SelfProxy]._height);
+    public [DocFields] = () => this[Self][FieldTuples]; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any);
+    public [Width] = () => NumCast(this[SelfProxy]._width);
+    public [Height] = () => NumCast(this[SelfProxy]._height);
     public [ToScriptString] = () => `idToDoc("${this[Self][Id]}")`;
     public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? '-inaccessible-' : this[SelfProxy].title})`;
-    public get [LayoutSym]() {
+    public get [DocLayout]() {
         return this[SelfProxy].__LAYOUT__;
     }
-    public get [DataSym](): Doc {
+    public get [DocData](): Doc {
         const self = this[SelfProxy];
         return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
     }
@@ -535,17 +530,17 @@ export namespace Doc {
         if (key.startsWith('_')) key = key.substring(1);
         const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined;
         const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
-        const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
+        const onProto = hasProto && Object.getOwnPropertyNames(hasProto).indexOf(key) !== -1;
         if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
             doc[key] = value;
-        } else doc.proto![key] = value;
+        } else hasProto[key] = value;
     }
     export function GetAllPrototypes(doc: Doc): Doc[] {
         const protos: Doc[] = [];
         let d: Opt<Doc> = doc;
         while (d) {
             protos.push(d);
-            d = FieldValue(d.proto);
+            d = DocCast(FieldValue(d.proto));
         }
         return protos;
     }
@@ -585,7 +580,7 @@ export namespace Doc {
     // 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 data doc ('isDataDoc').
     export function GetProto(doc: Doc): Doc {
-        const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : doc.proto || doc);
+        const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : DocCast(doc.proto, doc));
         return proto === doc ? proto : Doc.GetProto(proto);
     }
     export function GetDataDoc(doc: Doc): Doc {
@@ -599,7 +594,7 @@ export namespace Doc {
         let proto: Doc | undefined = doc;
         while (proto) {
             Object.keys(proto).forEach(key => results.add(key));
-            proto = proto.proto;
+            proto = DocCast(FieldValue(proto.proto));
         }
 
         return Array.from(results);
@@ -676,7 +671,7 @@ export namespace Doc {
         const bounds = docList.reduce(
             (bounds, doc) => {
                 const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
-                const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+                const [bptX, bptY] = [sptX + doc[Width](), sptY + doc[Height]()];
                 return {
                     x: Math.min(sptX, bounds.x),
                     y: Math.min(sptY, bounds.y),
@@ -700,7 +695,7 @@ export namespace Doc {
         embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
         embedding.author = Doc.CurrentUserEmail;
 
-        Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'proto_embeddings', embedding);
+        Doc.AddDocToList(Doc.GetProto(doc)[DocData], 'proto_embeddings', embedding);
 
         return embedding;
     }
@@ -772,7 +767,7 @@ export namespace Doc {
                 }
             })
         );
-        Array.from(doc[DirectLinksSym]).forEach(async link => {
+        Array.from(doc[DirectLinks]).forEach(async link => {
             if (
                 cloneLinks ||
                 ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) &&
@@ -930,7 +925,7 @@ export namespace Doc {
             if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc))) {
                 expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
             } else {
-                templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
+                templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype
                 if (!targetDoc[expandedLayoutFieldKey]) {
                     _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
                     setTimeout(
@@ -969,7 +964,7 @@ export namespace Doc {
             const field = ProxyField.WithoutProxy(() => doc[key]);
             if (key === 'proto' && copyProto) {
                 if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) {
-                    overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto);
+                    overwrite[key] = Doc.Overwrite(doc.proto, overwrite.proto);
                 }
             } else {
                 if (field instanceof RefField) {
@@ -991,38 +986,37 @@ export namespace Doc {
         const copy = new Doc(copyProtoId, true);
         updateCachedAcls(copy);
         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]));
-            const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
-            if (key === 'proto' && copyProto) {
-                if (doc[key] instanceof Doc) {
-                    copy[key] = Doc.MakeCopy(doc[key]!, false);
-                }
-            } else {
-                if (field instanceof RefField) {
-                    copy[key] = field;
-                } else if (cfield instanceof ComputedField) {
-                    copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
-                } else if (field instanceof ObjectField) {
-                    copy[key] =
-                        doc[key] instanceof Doc
-                            ? key.includes('layout[')
-                                ? undefined
-                                : doc[key] // reference documents except remove documents that are expanded teplate fields
-                            : ObjectField.MakeCopy(field);
-                } else if (field instanceof Promise) {
-                    debugger; //This shouldn't happend...
+        Object.keys(doc)
+            .filter(key => !exclude.includes(key))
+            .forEach(key => {
+                if (key === 'proto' && copyProto) {
+                    if (doc.proto instanceof Doc) {
+                        copy[key] = Doc.MakeCopy(doc.proto, false);
+                    }
                 } else {
-                    copy[key] = field;
+                    const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+                    const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+                    if (field instanceof RefField) {
+                        copy[key] = field;
+                    } else if (cfield instanceof ComputedField) {
+                        copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
+                    } else if (field instanceof ObjectField) {
+                        copy[key] =
+                            doc[key] instanceof Doc && key.includes('layout[')
+                                ? undefined // remove expanded template field documents
+                                : ObjectField.MakeCopy(field);
+                    } else if (field instanceof Promise) {
+                        debugger; //This shouldn't happend...
+                    } else {
+                        copy[key] = field;
+                    }
                 }
-            }
-        });
+            });
         if (copyProto) {
             Doc.GetProto(copy).embedContainer = undefined;
             Doc.GetProto(copy).proto_embeddings = new List<Doc>([copy]);
         } else {
-            Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'proto_embeddings', copy);
+            Doc.AddDocToList(Doc.GetProto(copy)[DocData], 'proto_embeddings', copy);
         }
         copy.embedContainer = undefined;
         Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
@@ -1045,7 +1039,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], 'proto_embeddings', delegate);
+            if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
             title && (delegate.title = title);
             delegate[Initializing] = false;
             Doc.AddFileOrphan(delegate);
@@ -1069,7 +1063,7 @@ export namespace Doc {
         delegate[Initializing] = true;
         delegate.proto = delegateProto;
         delegate.author = Doc.CurrentUserEmail;
-        Doc.AddDocToList(delegateProto[DataSym], 'proto_embeddings', delegate);
+        Doc.AddDocToList(delegateProto[DocData], 'proto_embeddings', delegate);
         delegate[Initializing] = false;
         delegateProto[Initializing] = false;
         return delegate;
@@ -1173,7 +1167,7 @@ export namespace Doc {
     }
     export const brushManager = new DocBrush();
 
-    export class DocData {
+    export class UserDocData {
         @observable _user_doc: Doc = undefined!;
         @observable _sharing_doc: Doc = undefined!;
         @observable _searchQuery: string = '';
@@ -1183,7 +1177,7 @@ export namespace Doc {
     // a layout field or 'layout' is given.
     export function Layout(doc: Doc, layout?: Doc): Doc {
         const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}-layout[` + layout[Id] + ']'], Doc, null);
-        return overrideLayout || doc[LayoutSym] || doc;
+        return overrideLayout || doc[DocLayout] || doc;
     }
     export function SetLayout(doc: Doc, layout: Doc | string) {
         doc[StrCast(doc.layout_fieldKey, 'layout')] = layout;
@@ -1198,12 +1192,12 @@ export namespace Doc {
         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[Width]() : 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 nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[Height]()) / doc[Width]();
+        const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? doc[Height]() : 0);
         return NumCast(doc._nativeHeight, nheight || dheight);
     }
     export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) {
@@ -1213,7 +1207,7 @@ export namespace Doc {
         doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
     }
 
-    const manager = new DocData();
+    const manager = new UserDocData();
     export function SearchQuery(): string {
         return manager._searchQuery;
     }
@@ -1348,16 +1342,16 @@ export namespace Doc {
     export var highlightedDocs = new ObservableSet<Doc>();
     export function IsHighlighted(doc: Doc) {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false;
-        return doc[HighlightSym] || Doc.GetProto(doc)[HighlightSym];
+        return doc[Highlight] || Doc.GetProto(doc)[Highlight];
     }
     export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presEffect?: Doc) {
         runInAction(() => {
             highlightedDocs.add(doc);
-            doc[HighlightSym] = true;
-            doc[AnimationSym] = presEffect;
+            doc[Highlight] = true;
+            doc[Animation] = presEffect;
             if (dataAndDisplayDocs) {
                 highlightedDocs.add(Doc.GetProto(doc));
-                Doc.GetProto(doc)[HighlightSym] = true;
+                Doc.GetProto(doc)[Highlight] = true;
             }
         });
     }
@@ -1367,8 +1361,8 @@ export namespace Doc {
             (doc ? [doc] : Array.from(highlightedDocs)).forEach(doc => {
                 highlightedDocs.delete(doc);
                 highlightedDocs.delete(Doc.GetProto(doc));
-                doc[HighlightSym] = Doc.GetProto(doc)[HighlightSym] = false;
-                doc[AnimationSym] = undefined;
+                doc[Highlight] = Doc.GetProto(doc)[Highlight] = false;
+                doc[Animation] = undefined;
             });
         });
     }
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
new file mode 100644
index 000000000..65decc147
--- /dev/null
+++ b/src/fields/DocSymbols.ts
@@ -0,0 +1,26 @@
+export const Update = Symbol('DocUpdate');
+export const Self = Symbol('DocSelf');
+export const SelfProxy = Symbol('DocSelfProxy');
+export const FieldKeys = Symbol('DocFieldKeys');
+export const FieldTuples = Symbol('DocFieldTuples');
+export const Width = Symbol('DocWidth');
+export const Height = Symbol('DocHeight');
+export const Animation = Symbol('DocAnimation');
+export const Highlight = Symbol('DocHighlight');
+export const DocData = Symbol('DocData');
+export const DocLayout = Symbol('DocLayout');
+export const DocFields = Symbol('DocFields');
+export const DocCss = Symbol('DocCss');
+export const DocAcl = Symbol('DocAcl');
+export const DirectLinks = Symbol('DocDirectLinks');
+export const AclUnset = Symbol('DocAclUnset');
+export const AclPrivate = Symbol('DocAclOwnerOnly');
+export const AclReadonly = Symbol('DocAclReadOnly');
+export const AclAugment = Symbol('DocAclAugment');
+export const AclSelfEdit = Symbol('DocAclSelfEdit');
+export const AclEdit = Symbol('DocAclEdit');
+export const AclAdmin = Symbol('DocAclAdmin');
+export const UpdatingFromServer = Symbol('DocUpdatingFromServer');
+export const Initializing = Symbol('DocInitializing');
+export const ForceServerWrite = Symbol('DocForceServerWrite');
+export const CachedUpdates = Symbol('DocCachedUpdates');
diff --git a/src/fields/FieldSymbols.ts b/src/fields/FieldSymbols.ts
index e50c2856f..c381f14f5 100644
--- a/src/fields/FieldSymbols.ts
+++ b/src/fields/FieldSymbols.ts
@@ -1,12 +1,9 @@
-export const Update = Symbol('Update');
-export const Self = Symbol('Self');
-export const SelfProxy = Symbol('SelfProxy');
-export const HandleUpdate = Symbol('HandleUpdate');
-export const Id = Symbol('Id');
-export const OnUpdate = Symbol('OnUpdate');
-export const Parent = Symbol('Parent');
-export const Copy = Symbol('Copy');
-export const ToValue = Symbol('ToValue');
-export const ToScriptString = Symbol('ToScriptString');
-export const ToPlainText = Symbol('ToPlainText');
-export const ToString = Symbol('ToString');
+export const HandleUpdate = Symbol('FieldHandleUpdate');
+export const Id = Symbol('FieldId');
+export const OnUpdate = Symbol('FieldOnUpdate');
+export const Parent = Symbol('FieldParent');
+export const Copy = Symbol('FieldCopy');
+export const ToValue = Symbol('FieldToValue');
+export const ToScriptString = Symbol('FieldToScriptString');
+export const ToPlainText = Symbol('FieldToPlainText');
+export const ToString = Symbol('FieldToString');
diff --git a/src/fields/List.ts b/src/fields/List.ts
index dfd24cf7a..033fa569b 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -3,8 +3,9 @@ import { alias, list, serializable } from 'serializr';
 import { DocServer } from '../client/DocServer';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { afterDocDeserialize, autoObject, Deserializable } from '../client/util/SerializationHelper';
+import { FieldTuples, Self, SelfProxy, Update } from './DocSymbols';
 import { Field } from './Doc';
-import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols';
+import { Copy, OnUpdate, Parent, ToScriptString, ToString } from './FieldSymbols';
 import { ObjectField } from './ObjectField';
 import { ProxyField } from './Proxy';
 import { RefField } from './RefField';
@@ -21,12 +22,12 @@ const listHandlers: any = {
         if (value instanceof RefField) {
             throw new Error('fill with RefFields not supported yet');
         }
-        const res = this[Self].__fields.fill(value, start, end);
+        const res = this[Self].__fieldTuples.fill(value, start, end);
         this[Update]();
         return res;
     },
     pop(): any {
-        const field = toRealField(this[Self].__fields.pop());
+        const field = toRealField(this[Self].__fieldTuples.pop());
         this[Update]();
         return field;
     },
@@ -34,7 +35,7 @@ const listHandlers: any = {
         items = items.map(toObjectField);
 
         const list = this[Self];
-        const length = list.__fields.length;
+        const length = list.__fieldTuples.length;
         for (let i = 0; i < items.length; i++) {
             const item = items[i];
             //TODO Error checking to make sure parent doesn't already exist
@@ -43,23 +44,23 @@ const listHandlers: any = {
                 item[OnUpdate] = updateFunction(list, i + length, item, this);
             }
         }
-        const res = list.__fields.push(...items);
+        const res = list.__fieldTuples.push(...items);
         this[Update]({ op: '$addToSet', items, length: length + items.length });
         return res;
     }),
     reverse() {
-        const res = this[Self].__fields.reverse();
+        const res = this[Self].__fieldTuples.reverse();
         this[Update]();
         return res;
     },
     shift() {
-        const res = toRealField(this[Self].__fields.shift());
+        const res = toRealField(this[Self].__fieldTuples.shift());
         this[Update]();
         return res;
     },
     sort(cmpFunc: any) {
         this[Self].__realFields(); // coerce retrieving entire array
-        const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
+        const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
         this[Update]();
         return res;
     },
@@ -67,7 +68,7 @@ const listHandlers: any = {
         this[Self].__realFields(); // coerce retrieving entire array
         items = items.map(toObjectField);
         const list = this[Self];
-        const removed = list.__fields.filter((item: any, i: number) => i >= start && i < start + deleteCount);
+        const removed = list.__fieldTuples.filter((item: any, i: number) => i >= start && i < start + deleteCount);
         for (let i = 0; i < items.length; i++) {
             const item = items[i];
             //TODO Error checking to make sure parent doesn't already exist
@@ -77,18 +78,18 @@ const listHandlers: any = {
                 item[OnUpdate] = updateFunction(list, i + start, item, this);
             }
         }
-        let hintArray: {val : any, index : number}[] = [];
-        for(let i = start; i < start + deleteCount; i++) {
-            hintArray.push({val : list.__fields[i], index : i});
+        let hintArray: { val: any; index: number }[] = [];
+        for (let i = start; i < start + deleteCount; i++) {
+            hintArray.push({ val: list.__fieldTuples[i], index: i });
         }
-        const res = list.__fields.splice(start, deleteCount, ...items);
+        const res = list.__fieldTuples.splice(start, deleteCount, ...items);
         // the hint object sends the starting index of the slice and the number
-        // of elements to delete. 
+        // of elements to delete.
         this[Update](
             items.length === 0 && deleteCount
-                ? { op: '$remFromSet', items: removed, hint : { start : start, deleteCount : deleteCount }, length: list.__fields.length }
-                : items.length && !deleteCount && start === list.__fields.length
-                ? { op: '$addToSet', items, length: list.__fields.length }
+                ? { op: '$remFromSet', items: removed, hint: { start: start, deleteCount: deleteCount }, length: list.__fieldTuples.length }
+                : items.length && !deleteCount && start === list.__fieldTuples.length
+                ? { op: '$addToSet', items, length: list.__fieldTuples.length }
                 : undefined
         );
         return res.map(toRealField);
@@ -96,7 +97,7 @@ const listHandlers: any = {
     unshift(...items: any[]) {
         items = items.map(toObjectField);
         const list = this[Self];
-        const length = list.__fields.length;
+        const length = list.__fieldTuples.length;
         for (let i = 0; i < items.length; i++) {
             const item = items[i];
             //TODO Error checking to make sure parent doesn't already exist
@@ -106,32 +107,32 @@ const listHandlers: any = {
                 item[OnUpdate] = updateFunction(list, i, item, this);
             }
         }
-        const res = this[Self].__fields.unshift(...items);
+        const res = this[Self].__fieldTuples.unshift(...items);
         this[Update]();
         return res;
     },
     /// Accessor methods
     concat: action(function (this: any, ...items: any[]) {
         this[Self].__realFields();
-        return this[Self].__fields.map(toRealField).concat(...items);
+        return this[Self].__fieldTuples.map(toRealField).concat(...items);
     }),
     includes(valueToFind: any, fromIndex: number) {
         if (valueToFind instanceof RefField) {
             return this[Self].__realFields().includes(valueToFind, fromIndex);
         } else {
-            return this[Self].__fields.includes(valueToFind, fromIndex);
+            return this[Self].__fieldTuples.includes(valueToFind, fromIndex);
         }
     },
     indexOf(valueToFind: any, fromIndex: number) {
         if (valueToFind instanceof RefField) {
             return this[Self].__realFields().indexOf(valueToFind, fromIndex);
         } else {
-            return this[Self].__fields.indexOf(valueToFind, fromIndex);
+            return this[Self].__fieldTuples.indexOf(valueToFind, fromIndex);
         }
     },
     join(separator: any) {
         this[Self].__realFields();
-        return this[Self].__fields.map(toRealField).join(separator);
+        return this[Self].__fieldTuples.map(toRealField).join(separator);
     },
     lastElement() {
         return this[Self].__realFields().lastElement();
@@ -140,12 +141,12 @@ const listHandlers: any = {
         if (valueToFind instanceof RefField) {
             return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex);
         } else {
-            return this[Self].__fields.lastIndexOf(valueToFind, fromIndex);
+            return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex);
         }
     },
     slice(begin: number, end: number) {
         this[Self].__realFields();
-        return this[Self].__fields.slice(begin, end).map(toRealField);
+        return this[Self].__fieldTuples.slice(begin, end).map(toRealField);
     },
 
     /// Iteration methods
@@ -156,55 +157,55 @@ const listHandlers: any = {
         return this[Self].__realFields().every(callback, thisArg);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+        // return this[Self].__fieldTuples.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
     },
     filter(callback: any, thisArg: any) {
         return this[Self].__realFields().filter(callback, thisArg);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+        // return this[Self].__fieldTuples.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
     },
     find(callback: any, thisArg: any) {
         return this[Self].__realFields().find(callback, thisArg);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+        // return this[Self].__fieldTuples.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
     },
     findIndex(callback: any, thisArg: any) {
         return this[Self].__realFields().findIndex(callback, thisArg);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+        // return this[Self].__fieldTuples.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
     },
     forEach(callback: any, thisArg: any) {
         return this[Self].__realFields().forEach(callback, thisArg);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+        // return this[Self].__fieldTuples.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
     },
     map(callback: any, thisArg: any) {
         return this[Self].__realFields().map(callback, thisArg);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+        // return this[Self].__fieldTuples.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
     },
     reduce(callback: any, initialValue: any) {
         return this[Self].__realFields().reduce(callback, initialValue);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+        // return this[Self].__fieldTuples.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
     },
     reduceRight(callback: any, initialValue: any) {
         return this[Self].__realFields().reduceRight(callback, initialValue);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+        // return this[Self].__fieldTuples.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
     },
     some(callback: any, thisArg: any) {
         return this[Self].__realFields().some(callback, thisArg);
         // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
         // If we don't want to support the array parameter, we should use this version instead
-        // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+        // return this[Self].__fieldTuples.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
     },
     values() {
         return this[Self].__realFields().values();
@@ -246,7 +247,7 @@ type ListUpdate<T> = ListSpliceUpdate<T> | ListIndexUpdate<T>;
 
 type StoredType<T extends Field> = T extends RefField ? ProxyField<T> : T;
 
-export const ListFieldName="fields";
+export const ListFieldName = 'fields';
 @Deserializable('list')
 class ListImpl<T extends Field> extends ObjectField {
     constructor(fields?: T[]) {
@@ -254,9 +255,9 @@ class ListImpl<T extends Field> extends ObjectField {
         const list = new Proxy<this>(this, {
             set: setter,
             get: listGetter,
-            ownKeys: target => Object.keys(target.__fields),
+            ownKeys: target => Object.keys(target.__fieldTuples),
             getOwnPropertyDescriptor: (target, prop) => {
-                if (prop in target.__fields) {
+                if (prop in target[FieldTuples]) {
                     return {
                         configurable: true, //TODO Should configurable be true?
                         enumerable: true,
@@ -281,7 +282,7 @@ class ListImpl<T extends Field> extends ObjectField {
     // this requests all ProxyFields at the same time to avoid the overhead
     // of separate network requests and separate updates to the React dom.
     private __realFields() {
-        const unrequested = this.__fields.filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
+        const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
         // if we find any ProxyFields that don't have a current value, then
         // start the server request for all of them
         if (unrequested.length) {
@@ -293,17 +294,16 @@ class ListImpl<T extends Field> extends ObjectField {
             // will await the batch request and return the requested field value.
             unrequested.forEach(p => p.setExternalValuePromise(allSetPromise));
         }
-        return this.__fields.map(toRealField);
+        return this[FieldTuples].map(toRealField);
     }
 
-    public static FieldDataName = 'fields';
     @serializable(alias(ListFieldName, list(autoObject(), { afterDeserialize: afterDocDeserialize })))
-    private get __fields() {
-        return this.___fields;
+    private get __fieldTuples() {
+        return this[FieldTuples];
     }
 
-    private set __fields(value) {
-        this.___fields = value;
+    private set __fieldTuples(value) {
+        this[FieldTuples] = value;
         for (const key in value) {
             const field = value[key];
             if (field instanceof ObjectField) {
@@ -314,14 +314,14 @@ class ListImpl<T extends Field> extends ObjectField {
     }
 
     [Copy]() {
-        const copiedData = this[Self].__fields.map(f => (f instanceof ObjectField ? f[Copy]() : f));
+        const copiedData = this[Self].__fieldTuples.map(f => (f instanceof ObjectField ? f[Copy]() : f));
         const deepCopy = new ListImpl<T>(copiedData as any);
         return deepCopy;
     }
 
     // @serializable(alias("fields", list(autoObject())))
     @observable
-    private ___fields: StoredType<T>[] = [];
+    private [FieldTuples]: StoredType<T>[] = [];
 
     private [Update] = (diff: any) => {
         // console.log(diff);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index a2b445d6c..62f27c7d2 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -7,28 +7,9 @@ import { SerializationHelper } from '../client/util/SerializationHelper';
 import { UndoManager } from '../client/util/UndoManager';
 import { returnZero } from '../Utils';
 import CursorField from './CursorField';
-import {
-    AclAdmin,
-    AclEdit,
-    aclLevel,
-    AclPrivate,
-    AclSelfEdit,
-    AclSym,
-    DataSym,
-    Doc,
-    DocListCast,
-    DocListCastAsync,
-    ForceServerWrite,
-    HeightSym,
-    HierarchyMapping,
-    Initializing,
-    LayoutSym,
-    ReverseHierarchyMap,
-    updateCachedAcls,
-    UpdatingFromServer,
-    WidthSym,
-} from './Doc';
-import { Id, OnUpdate, Parent, SelfProxy, ToValue, Update } from './FieldSymbols';
+import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, 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';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
@@ -56,7 +37,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     if (value !== undefined) {
         value = value[SelfProxy] || value;
     }
-    const curValue = target.__fields[prop];
+    const curValue = target.__fieldTuples[prop];
     if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) {
         // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically
         // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way
@@ -88,11 +69,11 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
 
     if (writeToDoc) {
         if (value === undefined) {
-            target.__fieldKeys && delete target.__fieldKeys[prop];
-            delete target.__fields[prop];
+            delete target[FieldKeys][prop];
+            delete target.__fieldTuples[prop];
         } else {
-            target.__fieldKeys && (target.__fieldKeys[prop] = true);
-            target.__fields[prop] = value;
+            target[FieldKeys][prop] = true;
+            target.__fieldTuples[prop] = value;
         }
 
         if (writeToServer) {
@@ -148,12 +129,12 @@ export function denormalizeEmail(email: string) {
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
     return;
-    const dataDoc = parent[DataSym];
-    for (const key of Object.keys(dataDoc)) {
-        // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
-        key.startsWith('acl') && distributeAcls(key, permission, child);
-    }
+    // const dataDoc = parent[DataSym];
+    // for (const key of Object.keys(dataDoc)) {
+    //     // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
+    //     const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
+    //     key.startsWith('acl') && distributeAcls(key, permission, child);
+    // }
 }
 
 /**
@@ -212,7 +193,7 @@ export function SetCachedGroups(groups: string[]) {
     runInAction(() => cachedGroups.push(...groups));
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
-    const targetAcls = target[AclSym];
+    const targetAcls = target[DocAcl];
     if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
 
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
@@ -237,7 +218,7 @@ function getEffectiveAcl(target: any, user?: string): symbol {
         return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
     }
     // authored documents are private until an ACL is set.
-    const targetAuthor = target.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author
+    const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author
     if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
     return AclAdmin;
 }
@@ -277,7 +258,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     }
 
     let dataDocChanged = false;
-    const dataDoc = target[DataSym];
+    const dataDoc = target[DocData];
     if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) {
         if (GetEffectiveAcl(dataDoc) === AclAdmin) {
             dataDoc[key] = acl;
@@ -290,13 +271,13 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         // maps over the children of the document
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
             distributeAcls(key, acl, d, inheritingFromCollection, visited);
-            distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited);
+            distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
         });
 
         // maps over the annotations of the document
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
             distributeAcls(key, acl, d, inheritingFromCollection, visited);
-            distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited);
+            distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
         });
     }
 
@@ -311,16 +292,16 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
     // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
     if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;
 
-    if (typeof prop === 'string' && prop !== '__id' && prop !== '__fields' && prop.startsWith('_')) {
+    if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) {
         if (!prop.startsWith('__')) prop = prop.substring(1);
         if (target.__LAYOUT__) {
             target.__LAYOUT__[prop] = value;
             return true;
         }
     }
-    if (target.__fields[prop] instanceof ComputedField) {
-        if (target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) {
-            return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
+    if (target.__fieldTuples[prop] instanceof ComputedField) {
+        if (target.__fieldTuples[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) {
+            return ScriptCast(target.__fieldTuples[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
         }
     }
     return _setter(target, prop, value, receiver);
@@ -330,14 +311,13 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
     // prettier-ignore
     switch (prop) {
         case 'then' :   return undefined;
-        case '__fields' :   case '__id':  
-        case 'constructor': case 'toString': case 'valueOf':  
-        case 'factory':     case 'serializeInfo': 
+        case 'constructor':   case 'toString': case 'valueOf':  
+        case 'serializeInfo': case 'factory':     
                         return target[prop];
-        case AclSym :   return target[AclSym];
-        case $mobx:     return target.__fields[prop];
-        case LayoutSym: return target.__LAYOUT__;
-        case HeightSym: case WidthSym: if (GetEffectiveAcl(target) === AclPrivate) return returnZero;
+        case DocAcl :   return target[DocAcl];
+        case $mobx:     return target.__fieldTuples[prop];
+        case DocLayout: return target.__LAYOUT__;
+        case Height: case Width: if (GetEffectiveAcl(target) === AclPrivate) return returnZero;
         default :
             if (typeof prop === 'symbol')  return target[prop];
             if (prop.startsWith('isMobX')) return target[prop];
@@ -351,7 +331,7 @@ export function getter(target: any, prop: string | symbol, proxy: any): any {
 }
 
 function getFieldImpl(target: any, prop: string | number, proxy: any, ignoreProto: boolean = false): any {
-    const field = target.__fields[prop];
+    const field = target.__fieldTuples[prop];
     const value = field?.[ToValue]?.(proxy); // converts ComputedFields to values, or unpacks ProxyFields into Proxys
     if (value) return value.value;
     if (field === undefined && !ignoreProto && prop !== 'proto') {
-- 
cgit v1.2.3-70-g09d2

