From f1ee3a6baffb22093d81e123d70f6ec9631c2075 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 15 Oct 2020 15:20:51 -0400
Subject: got rid of isDragging in favor of a field on the PresElement.  fixed
 moving items in presBox by fixing websocket to maintain ordering on
 sequential operations on a list.  fixed list splicing to not call addToSet
 unless splicing is at end of list.  fixed setting return scale when following
 link into portal

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8b9e84bd6..2d770898b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -922,6 +922,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
         } else {
             const layoutdoc = Doc.Layout(doc);
+            const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
 
             willZoom && this.setScaleToZoom(layoutdoc, scale);
             const newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (NumCast(this.props.Document._nativeWidth)) / 2 / this.zoomScaling() : 0);
@@ -930,7 +931,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
             HistoryUtil.pushState(newState);
 
-            const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
             if (DocListCast(this.dataDoc[this.props.annotationsKey || this.props.fieldKey]).includes(doc)) {
                 // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
                 if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
-- 
cgit v1.2.3-70-g09d2


From acccc301ed77d6ea15f025ff746138911b9f9c16 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 16 Oct 2020 02:02:10 -0400
Subject: fixed RTF so that you can scroll smoothly in a presentation.

---
 src/client/util/DocumentManager.ts                        |  2 +-
 src/client/views/collections/CollectionMenu.tsx           |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx         |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx                 |  2 +-
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 15 +++++++++++++--
 .../views/nodes/formattedText/FormattedTextBoxComment.tsx |  2 +-
 src/client/views/pdf/PDFViewer.tsx                        |  6 +++---
 7 files changed, 21 insertions(+), 10 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index a408e1df6..dc911ea75 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -190,7 +190,7 @@ export class DocumentManager {
                 highlight();
             } else {  // otherwise try to get a view of the context of the target
                 const targetDocContextView = getFirstDocView(targetDocContext);
-                targetDocContext._scrollY = targetDocContext._scrollPY = NumCast(targetDocContext._scrollTop, 0);  // this will force PDFs to activate and load their annotations / allow scrolling
+                targetDocContext._scrollY = targetDocContext._scrollPreviewY = NumCast(targetDocContext._scrollTop, 0);  // this will force PDFs to activate and load their annotations / allow scrolling
                 if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
                     targetDocContext._viewTransition = "transform 500ms";
                     targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom);
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index d9924c299..b2b1b7d25 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -400,7 +400,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
         if (targetDoc) {
             TabDocView.PinDoc(targetDoc, false);
             const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
-            if (targetDoc.type === DocumentType.PDF) {
+            if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB) {
                 const scroll = targetDoc._scrollTop;
                 activeDoc.presPinView = true;
                 activeDoc.presPinViewScroll = scroll;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2d770898b..b32a3bd52 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -908,7 +908,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     scrollTo = Math.max(0, NumCast(doc.y) - 50);
                 }
                 if (curScroll !== scrollTo || this.props.Document._viewTransition) {
-                    this.props.Document._scrollPY = this.props.Document._scrollY = scrollTo;
+                    this.props.Document._scrollPreviewY = this.props.Document._scrollY = scrollTo;
                     delay = Math.abs(scrollTo - curScroll) > 5 ? 1000 : 0;
                     !dontCenter && this.props.focus(this.props.Document);
                     afterFocus && setTimeout(afterFocus, delay);
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 8f9cb5b0e..867be9735 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -64,7 +64,7 @@ export class LinkDocPreview extends React.Component<Props> {
                 this._toolTipText = "";
                 LinkDocPreview.TargetDoc = this._targetDoc = target;
                 if (anchor !== this._targetDoc && anchor && this._targetDoc) {
-                    this._targetDoc._scrollPY = NumCast(anchor?.y);
+                    this._targetDoc._scrollPreviewY = NumCast(anchor?.y);
                 }
             });
         }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c3946dd57..903bbaaa3 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -23,7 +23,7 @@ import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { makeInterface } from "../../../../fields/Schema";
 import { Cast, DateCast, NumCast, StrCast, ScriptCast, BoolCast } from "../../../../fields/Types";
 import { TraceMobx, GetEffectiveAcl } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents, OmitKeys } from '../../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents, OmitKeys, smoothScroll } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from '../../../documents/Documents';
@@ -961,7 +961,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop),
-            pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true }
+            pos => this._scrollRef.current?.scrollTo({ top: pos }), { fireImmediately: true }
+        );
+        this._disposers.scrollY = reaction(() => Cast(this.layoutDoc._scrollY, "number", null),
+            scrollY => {
+                if (scrollY !== undefined) {
+                    const delay = this._scrollRef.current ? 0 : 250; // wait for mainCont and try again to scroll
+                    const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
+                    const duration = durationStr ? Number(durationStr[1]) : 1000;
+                    setTimeout(() => this._scrollRef.current && smoothScroll(duration, this._scrollRef.current, Math.abs(scrollY || 0)), delay);
+                    setTimeout(() => { this.Document._scrollTop = scrollY; this.Document._scrollY = undefined; }, duration + delay);
+                }
+            }, { fireImmediately: true }
         );
 
         setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight)));
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 0919b2b14..32038d1ee 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -260,7 +260,7 @@ export class FormattedTextBoxComment {
                             const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
                             const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
                             if (anchor !== target && anchor && target) {
-                                target._scrollPY = NumCast(anchor?.y);
+                                target._scrollPreviewY = NumCast(anchor?.y);
                             }
                             if (target?.author) {
                                 FormattedTextBoxComment.showCommentbox("", view, nbef);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 375748311..a8a6e8c33 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -190,15 +190,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
             },
             { fireImmediately: true }
         );
-        this._disposers.scrollPY = reaction(
-            () => Cast(this.Document._scrollPY, "number", null),
+        this._disposers.scrollPreviewY = reaction(
+            () => Cast(this.Document._scrollPreviewY, "number", null),
             (scrollY) => {
                 if (scrollY !== undefined) {
                     (this._showCover || this._showWaiting) && this.setupPdfJsViewer();
                     if (this.props.renderDepth === -1 && scrollY >= 0) {
                         if (!this._mainCont.current) setTimeout(() => this._mainCont.current && smoothScroll(1000, this._mainCont.current, scrollY || 0));
                         else smoothScroll(1000, this._mainCont.current, scrollY || 0);
-                        this.Document._scrollPY = undefined;
+                        this.Document._scrollPreviewY = undefined;
                     }
                 }
             },
-- 
cgit v1.2.3-70-g09d2


From 223262242f7db021c8449e920645892cc5ed2820 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 17 Oct 2020 17:45:10 -0400
Subject: major rewrite of native width/height/aspect.  Fixed scaling of text
 note sidebars.

---
 src/client/views/DocumentDecorations.tsx           | 39 ++++++++++------------
 src/client/views/PropertiesView.tsx                | 26 ++++++---------
 .../views/collections/CollectionLinearView.tsx     |  1 -
 .../views/collections/CollectionSchemaCells.tsx    |  6 ++--
 .../views/collections/CollectionStackingView.tsx   | 24 ++++++-------
 src/client/views/collections/TabDocView.tsx        | 10 +++---
 src/client/views/collections/TreeView.tsx          | 20 +++++------
 .../CollectionFreeFormLayoutEngines.tsx            |  8 ++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 25 +++++++-------
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  4 +--
 .../views/nodes/ContentFittingDocumentView.tsx     |  8 ++---
 src/client/views/nodes/DocumentView.tsx            | 22 ++++++------
 src/client/views/nodes/ImageBox.tsx                | 13 +++-----
 src/client/views/nodes/PDFBox.tsx                  | 11 +++---
 src/client/views/nodes/ScreenshotBox.tsx           | 14 ++++----
 src/client/views/nodes/VideoBox.tsx                | 34 +++++++++----------
 src/client/views/nodes/WebBox.tsx                  | 38 +++++++++------------
 .../views/nodes/formattedText/DashDocView.tsx      |  2 +-
 .../nodes/formattedText/FormattedTextBox.scss      |  1 -
 .../views/nodes/formattedText/FormattedTextBox.tsx | 17 +++++-----
 .../formattedText/FormattedTextBoxComment.tsx      |  4 +--
 .../views/nodes/formattedText/RichTextSchema.tsx   |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 | 22 ++++++------
 src/client/views/search/SearchBox.tsx              |  2 +-
 src/fields/Doc.ts                                  | 14 ++++++--
 25 files changed, 176 insertions(+), 191 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 66f0dd99d..4dcae8e60 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -327,8 +327,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height) {
                 this._inkDocs.push({ x: doc.x, y: doc.y, width: doc._width, height: doc._height });
                 if (InkStrokeProperties.Instance?._lock) {
-                    doc._nativeHeight = doc._height;
-                    doc._nativeWidth = doc._width;
+                    Doc.SetNativeHeight(doc, doc._height);
+                    Doc.SetNativeWidth(doc, doc._width);
                 }
             }
         }));
@@ -356,11 +356,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
         const first = SelectionManager.SelectedDocuments()[0];
         let thisPt = { thisX: e.clientX - this._offX, thisY: e.clientY - this._offY };
-        var fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0;
+        var fixedAspect = Doc.NativeAspect(first.layoutDoc);
         SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
             const doc = Document(element.rootDoc);
             if (doc.type === DocumentType.INK && doc._width && doc._height && InkStrokeProperties.Instance?._lock) {
-                fixedAspect = NumCast(doc._nativeWidth) / NumCast(doc._nativeHeight);
+                fixedAspect = Doc.NativeHeight(doc);
             }
         }));
 
@@ -394,7 +394,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 ((element.rootDoc.type === DocumentType.RTF ||
                     element.rootDoc.type === DocumentType.COMPARISON ||
                     (element.rootDoc.type === DocumentType.WEB && Doc.LayoutField(element.rootDoc) instanceof HtmlField))
-                    && element.layoutDoc._nativeHeight) && element.toggleNativeDimensions()));
+                    && Doc.NativeHeight(element.layoutDoc)) && element.toggleNativeDimensions()));
         switch (this._resizeHdlId) {
             case "": break;
             case "documentDecorations-topLeftResizer":
@@ -440,11 +440,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         }
 
         SelectionManager.SelectedDocuments().forEach(action((docView: DocumentView) => {
-            if (e.ctrlKey && !docView.props.Document._nativeHeight) docView.toggleNativeDimensions();
+            if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 const doc = Document(docView.rootDoc);
-                let nwidth = returnVal(docView.NativeWidth?.(), doc._nativeWidth);
-                let nheight = returnVal(docView.NativeHeight?.(), doc._nativeHeight);
+                let nwidth = returnVal(docView.NativeWidth?.(), Doc.NativeWidth(doc));
+                let nheight = returnVal(docView.NativeHeight?.(), Doc.NativeHeight(doc));
                 const width = (doc._width || 0);
                 let height = (doc._height || (nheight / nwidth * width));
                 height = !height || isNaN(height) ? 20 : height;
@@ -463,26 +463,23 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 const fixedAspect = (nwidth && nheight);
-                const fieldKey = docView.LayoutFieldKey;
                 if (fixedAspect && (!nwidth || !nheight)) {
-                    doc[DataSym][fieldKey + "-nativeWidth"] = doc._nativeWidth = nwidth = doc._width || 0;
-                    doc[DataSym][fieldKey + "-nativeHeight"] = doc._nativeHeight = nheight = doc._height || 0;
+                    doc._nativeWidth = nwidth = doc._width || 0;
+                    doc._nativeHeight = nheight = doc._height || 0;
                 }
-                const anno = Cast(doc.annotationOn, Doc, null);
-                if (e.ctrlKey && (anno || doc.type === DocumentType.IMG)) {
+                if (e.ctrlKey && [DocumentType.IMG, DocumentType.SCREENSHOT, DocumentType.VID].includes(doc.type as DocumentType)) {
                     dW !== 0 && runInAction(() => {
-                        const dataDoc = (anno ?? doc)[DataSym];
-                        const annoFieldKey = Doc.LayoutFieldKey(anno ?? doc);
-                        const nw = NumCast(dataDoc[annoFieldKey + "-nativeWidth"]);
-                        const nh = NumCast(dataDoc[annoFieldKey + "-nativeHeight"]);
-                        dataDoc[annoFieldKey + "-nativeWidth"] = nw + (dW > 0 ? 10 : -10);
-                        dataDoc[annoFieldKey + "-nativeHeight"] = nh + (dW > 0 ? 10 : -10) * nh / nw;
+                        const dataDoc = doc[DataSym];
+                        const nw = Doc.NativeWidth(dataDoc);
+                        const nh = Doc.NativeHeight(dataDoc);
+                        Doc.SetNativeWidth(dataDoc, nw + (dW > 0 ? 10 : -10));
+                        Doc.SetNativeHeight(dataDoc, nh + (dW > 0 ? 10 : -10) * nh / nw);
                     });
                 }
                 else if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH) || dragRight) {
                         if (!fixedAspect || (dragRight && e.ctrlKey)) {
-                            doc[DataSym][fieldKey + "-nativeWidth"] = doc._nativeWidth = actualdW / (doc._width || 1) * (doc._nativeWidth || 0);
+                            doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc);
                         }
                         doc._width = actualdW;
                         if (fixedAspect && !doc._fitWidth) doc._height = nheight / nwidth * doc._width;
@@ -490,7 +487,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     }
                     else {
                         if (!fixedAspect || (dragBottom && (e.ctrlKey || docView.layoutDoc._fitWidth))) {
-                            doc[DataSym][fieldKey + "-nativeHeight"] = doc._nativeHeight = actualdH / (doc._height || 1) * (doc._nativeHeight || 0);
+                            doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
                         }
                         doc._height = actualdH;
                         if (fixedAspect && !doc._fitWidth) doc._width = nwidth / nheight * doc._height;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 6bdb6e21f..39a4d293b 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -91,9 +91,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     docWidth = () => {
         if (this.selectedDoc) {
             const layoutDoc = this.selectedDoc;
-            const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
-            if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.width - 20));
-            return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20;
+            const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._fitWidth);
+            if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.width - 20));
+            return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20;
         } else {
             return 0;
         }
@@ -103,17 +103,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     docHeight = () => {
         if (this.selectedDoc && this.dataDoc) {
             const layoutDoc = this.selectedDoc;
-            return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
-                const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
-                if (aspect) return this.docWidth() * aspect;
-                return layoutDoc._fitWidth ? (!this.dataDoc._nativeHeight ? NumCast(this.props.height) :
-                    Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
-                        NumCast(this.props.height)))) :
-                    NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
-            })()));
-        } else {
-            return 0;
+            return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT,
+                Doc.NativeAspect(layoutDoc, undefined, true) ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true) :
+                    layoutDoc._fitWidth ? (!Doc.NativeHeight(this.dataDoc) ? NumCast(this.props.height) :
+                        Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))) :
+                        NumCast(layoutDoc._height) || 50));
         }
+        return 0;
     }
 
     @computed get expandedField() {
@@ -972,7 +968,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         </div>
                         {this.openContexts ? <div className="propertiesView-contexts-content"  >{this.contexts}</div> : null}
                     </div>
-                    {/* <div className="propertiesView-layout">
+                    <div className="propertiesView-layout">
                         <div className="propertiesView-layout-title"
                             onPointerDown={action(() => this.openLayout = !this.openLayout)}
                             style={{ backgroundColor: this.openLayout ? "black" : "" }}>
@@ -982,7 +978,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             </div>
                         </div>
                         {this.openLayout ? <div className="propertiesView-layout-content"  >{this.layoutPreview}</div> : null}
-                    </div> */}
+                    </div>
                 </div>;
             }
             if (this.isPres) {
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 0eac5136a..b6ab3f0e0 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -129,7 +129,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                     {this.childLayoutPairs.map((pair, ind) => {
                         const nested = pair.layout._viewType === CollectionViewType.Linear;
                         const dref = React.createRef<HTMLDivElement>();
-                        const nativeWidth = NumCast(pair.layout._nativeWidth, this.dimension());
                         const scalable = pair.layout.onClick || pair.layout.onDragStart;
                         return <div className={`collectionLinearView-docBtn` + (scalable ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
                             style={{
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 2896a8243..0b3dfe1e4 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -398,10 +398,10 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell {
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
         const url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
 
-        const heightToWidth = NumCast(this._rowDoc._nativeHeight) / NumCast(this._rowDoc._nativeWidth);
+        const aspect = Doc.NativeAspect(this._rowDoc);
         let width = Math.min(75, this.props.rowProps.width);
-        const height = Math.min(75, width * heightToWidth);
-        width = height / heightToWidth;
+        const height = Math.min(75, width / aspect);
+        width = height * aspect;
 
         const reference = React.createRef<HTMLDivElement>();
         return <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index fc4ca3100..b62fde4c8 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -59,7 +59,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
     @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
     @computed get showAddAGroup() { return (this.pivotField && (this.chromeStatus !== 'view-mode' && this.chromeStatus !== 'disabled')); }
     @computed get columnWidth() {
-        return Math.min(this.props.PanelWidth() / this.props.ContentScaling() - 2 * this.xMargin,
+        return Math.min(this.props.PanelWidth() / this.props.ContentScaling() /* / NumCast(this.layoutDoc._viewScale, 1)*/ - 2 * this.xMargin,
             this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
     }
     @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
@@ -233,27 +233,27 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
     getDocWidth(d?: Doc) {
         if (!d) return 0;
         const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
-        const nw = NumCast(layoutDoc._nativeWidth);
+        const nw = Doc.NativeWidth(layoutDoc);
         return Math.min(nw && !this.layoutDoc._columnsFill ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
     }
     getDocHeight(d?: Doc) {
         if (!d) return 0;
-        const dataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
-        const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
-        const layoutField = Doc.LayoutFieldKey(layoutDoc);
-        const nw = NumCast(layoutDoc._nativeWidth) || NumCast(dataDoc?.[`${layoutField}-nativeWidth`]);
-        const nh = NumCast(layoutDoc._nativeHeight) || NumCast(dataDoc?.[`${layoutField}-nativeHeight`]);
+        const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
+        const childLayoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.());
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc);
         let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
+        if (!this.layoutDoc._columnsFill) wid = Math.min(wid, childLayoutDoc[WidthSym]());
         const hllimit = NumCast(this.layoutDoc.childLimitHeight, -1);
-        if (!layoutDoc._fitWidth && nw && nh) {
+        if (!childLayoutDoc._fitWidth && nw && nh) {
             const aspect = nw && nh ? nh / nw : 1;
             if (!(this.layoutDoc._columnsFill)) wid = Math.min(this.getDocWidth(d), wid);
             return Math.min(hllimit === 0 ? this.props.PanelWidth() : hllimit === -1 ? 10000 : hllimit, wid * aspect);
         }
-        return layoutDoc._fitWidth ?
+        return childLayoutDoc._fitWidth ?
             (!nh ? this.props.PanelHeight() - 2 * this.yMargin :
                 Math.min(wid * nh / (nw || 1), this.layoutDoc._autoHeight ? 100000 : this.props.PanelHeight() - 2 * this.yMargin)) :
-            Math.min(hllimit === 0 ? this.props.PanelWidth() : hllimit === -1 ? 10000 : hllimit, Math.max(20, layoutDoc[HeightSym]()));
+            Math.min(hllimit === 0 ? this.props.PanelWidth() : hllimit === -1 ? 10000 : hllimit, Math.max(20, childLayoutDoc[HeightSym]()));
     }
 
     columnDividerDown = (e: React.PointerEvent) => {
@@ -465,8 +465,8 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
     }
 
 
-    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc._nativeWidth)); }
-    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc._nativeHeight)); }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc)); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc)); }
 
     @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; }
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 0c7f39dc7..cd84d2d78 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -191,14 +191,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     }
 
     nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0;
-    panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
+    panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), Doc.NativeWidth(this.layoutDoc)), this._panelWidth) :
         (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth)
     panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
-    nativeWidth = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeWidth) || this._panelWidth : 0;
-    nativeHeight = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeHeight) || this._panelHeight : 0;
+    nativeWidth = () => !this.layoutDoc?._fitWidth ? Doc.NativeWidth(this.layoutDoc) || this._panelWidth : 0;
+    nativeHeight = () => !this.layoutDoc?._fitWidth ? Doc.NativeHeight(this.layoutDoc) || this._panelHeight : 0;
     ContentScaling = () => {
-        const nativeH = NumCast(this.layoutDoc?._nativeHeight);
-        const nativeW = NumCast(this.layoutDoc?._nativeWidth);
+        const nativeW = Doc.NativeWidth(this.layoutDoc);
+        const nativeH = Doc.NativeHeight(this.layoutDoc);
         let scaling = 1;
         if (nativeW && (this.layoutDoc?._fitWidth || this._panelHeight / nativeH > this._panelWidth / nativeW)) {
             scaling = this._panelWidth / nativeW;  // width-limited or fitWidth
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 1dfa5615a..925eb4be6 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -279,21 +279,21 @@ export class TreeView extends React.Component<TreeViewProps> {
     getTransform = () => this.refTransform(this._tref.current!);
     docWidth = () => {
         const layoutDoc = this.layoutDoc;
-        const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
-        if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
-        return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
+        const aspect = Doc.NativeAspect(layoutDoc);
+        if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - 20));
+        return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
     }
     docHeight = () => {
         const layoutDoc = this.layoutDoc;
         const bounds = this.boundsOfCollectionDocument;
         return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
-            const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
-            if (aspect) return this.docWidth() * aspect;
+            const aspect = Doc.NativeAspect(layoutDoc);
+            if (aspect) return this.docWidth() / (aspect || 1);
             if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
-            return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) :
-                Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
-                    NumCast(this.props.containingCollection._height)))) :
-                NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
+            return layoutDoc._fitWidth ? (!Doc.NativeHeight(this.doc) ? NumCast(this.props.containingCollection._height) :
+                Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) /
+                    (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containingCollection._height))
+                )) : (layoutDoc[HeightSym]() || 50);
         })()));
     }
 
@@ -728,7 +728,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
             const childLayout = Doc.Layout(pair.layout);
             const rowHeight = () => {
-                const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0);
+                const aspect = Doc.NativeAspect(childLayout);
                 return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
             };
             return <TreeView key={child[Id]}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index bc2cb2d20..edf473a90 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -241,10 +241,10 @@ export function computePivotLayout(
         val.docs.forEach((doc, i) => {
             const layoutDoc = Doc.Layout(doc);
             let wid = pivotAxisWidth;
-            let hgt = layoutDoc._nativeWidth ? (NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth)) * pivotAxisWidth : pivotAxisWidth;
+            let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
             if (hgt > pivotAxisWidth) {
                 hgt = pivotAxisWidth;
-                wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
+                wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
             }
             docMap.set(doc[Id] + (val.replicas || ""), {
                 x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0),
@@ -368,10 +368,10 @@ export function computeTimelineLayout(
             const stack = findStack(x, stacking);
             const layoutDoc = Doc.Layout(doc);
             let wid = pivotAxisWidth;
-            let hgt = layoutDoc._nativeWidth ? (NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth)) * pivotAxisWidth : pivotAxisWidth;
+            let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
             if (hgt > pivotAxisWidth) {
                 hgt = pivotAxisWidth;
-                wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
+                wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
             }
             docMap.set(doc[Id], {
                 x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b32a3bd52..c863addd2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -105,11 +105,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable canPanY: boolean = true;
 
     @computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; }
-    @computed get fitToContent() { return this.props.fitToBox || (this.Document._fitToBox && !this.isAnnotationOverlay); }
+    @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; }
     @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent ? this.props.ContentScaling() : 1; }
     @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); }
-    @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth)); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight)); }
+    @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document)); }
+    @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document)); }
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
     private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
@@ -120,7 +120,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (this.fitToContent) {
             const zs = !this.childDocs.length ? 1 :
                 Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x));
-            return mult * (this.props.isAnnotationOverlay ? Math.min(zs, 1) : zs);
+            return mult * zs;
         }
         return mult * NumCast(this.Document[this.scaleFieldKey], 1);
     }
@@ -219,7 +219,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 d.x = x + NumCast(d.x) - dropPos[0];
                 d.y = y + NumCast(d.y) - dropPos[1];
             }
-            const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)];
+            const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
             !d._isBackground && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
@@ -925,8 +925,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
 
             willZoom && this.setScaleToZoom(layoutdoc, scale);
-            const newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (NumCast(this.props.Document._nativeWidth)) / 2 / this.zoomScaling() : 0);
-            const newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (NumCast(this.props.Document._nativeHeight)) / 2 / this.zoomScaling() : 0);
+            const newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeWidth(this.props.Document)) / 2 / this.zoomScaling() : 0);
+            const newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (Doc.NativeHeight(this.props.Document)) / 2 / this.zoomScaling() : 0);
             const newState = HistoryUtil.getState();
             newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
             HistoryUtil.pushState(newState);
@@ -954,8 +954,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     setScaleToZoom = (doc: Doc, scale: number = 0.75) => {
-        const pw = this.isAnnotationOverlay ? NumCast(this.props.Document._nativeWidth) : this.props.PanelWidth();
-        const ph = this.isAnnotationOverlay ? NumCast(this.props.Document._nativeHeight) : this.props.PanelHeight();
+        const pw = this.isAnnotationOverlay ? Doc.NativeWidth(this.props.Document) : this.props.PanelWidth();
+        const ph = this.isAnnotationOverlay ? Doc.NativeHeight(this.props.Document) : this.props.PanelHeight();
         pw && ph && (this.Document[this.scaleFieldKey] = scale * Math.min(pw / NumCast(doc._width), ph / NumCast(doc._height)));
     }
 
@@ -979,7 +979,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             LayoutTemplate: childLayout.z ? undefined : this.props.ChildLayoutTemplate,
             LayoutTemplateString: childLayout.z ? undefined : this.props.ChildLayoutString,
             FreezeDimensions: this.props.freezeChildDimensions,
-            layoutKey: StrCast(this.props.Document.childLayoutKey),
             setupDragLines: this.setupDragLines,
             dontRegisterView: this.props.dontRegisterView,
             rootSelected: childData ? this.rootSelected : returnFalse,
@@ -1297,7 +1296,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
         this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
         if (!Doc.UserDoc().noviceMode) {
-            optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
+            optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
             optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" });
 
         }
@@ -1454,8 +1453,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @computed get contentScaling() {
         if (this.props.annotationsKey && !this.props.forceScaling) return 0;
-        const nw = returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth));
-        const nh = returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight));
+        const nw = returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document));
+        const nh = returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document));
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
         return wscale < hscale ? wscale : hscale;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5a8ce4e14..7d5c224bd 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -55,8 +55,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
     @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
     @computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); }
-    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc._nativeWidth, this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
-    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc._nativeHeight, this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, undefined, this.freezeDimensions)); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, undefined, this.freezeDimensions)); }
 
     public static getValues(doc: Doc, time: number) {
         const timecode = Math.round(time);
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 09051da78..bc3926ff4 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -22,12 +22,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
     }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
 
-    nativeWidth = () => returnVal(this.props.NativeWidth?.(),
-        NumCast(this.layoutDoc?._nativeWidth || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeWidth"],
-            (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth())))
-    nativeHeight = () => returnVal(this.props.NativeHeight?.(),
-        NumCast(this.layoutDoc?._nativeHeight || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeHeight"],
-            (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight())))
+    nativeWidth = () => returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || this.props.PanelWidth());
+    nativeHeight = () => returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || this.props.PanelHeight());
     @computed get scaling() {
         const wscale = this.props.PanelWidth() / this.nativeWidth();
         const hscale = this.props.PanelHeight() / this.nativeHeight();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e980322d5..d227d3a8e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -129,8 +129,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     @computed get topMost() { return this.props.renderDepth === 0; }
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
-    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
-    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
+    @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
+    @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
     @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
     @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
     @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@@ -494,8 +494,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 const doc = Document(this.props.Document);
                 const layoutDoc = Document(Doc.Layout(this.props.Document));
-                let nwidth = layoutDoc._nativeWidth || 0;
-                let nheight = layoutDoc._nativeHeight || 0;
+                let nwidth = Doc.NativeWidth(layoutDoc);
+                let nheight = Doc.NativeHeight(layoutDoc);
                 const width = (layoutDoc._width || 0);
                 const height = (layoutDoc._height || (nheight / nwidth * width));
                 const scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling();
@@ -505,13 +505,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 const fixedAspect = e.ctrlKey || (nwidth && nheight);
                 if (fixedAspect && (!nwidth || !nheight)) {
-                    layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0;
-                    layoutDoc._nativeHeight = nheight = layoutDoc._height || 0;
+                    Doc.SetNativeWidth(layoutDoc, nwidth = layoutDoc._width || 0);
+                    Doc.SetNativeHeight(layoutDoc, nheight = layoutDoc._height || 0);
                 }
                 if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH)) {
                         if (!fixedAspect) {
-                            layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0);
+                            Doc.SetNativeWidth(layoutDoc, actualdW / (layoutDoc._width || 1) * Doc.NativeWidth(layoutDoc));
                         }
                         layoutDoc._width = actualdW;
                         if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width;
@@ -519,7 +519,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     }
                     else {
                         if (!fixedAspect) {
-                            layoutDoc._nativeHeight = actualdH / (layoutDoc._height || 1) * (doc._nativeHeight || 0);
+                            Doc.SetNativeHeight(layoutDoc, actualdH / (layoutDoc._height || 1) * Doc.NativeHeight(doc));
                         }
                         layoutDoc._height = actualdH;
                         if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height;
@@ -754,8 +754,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this.props.bringToFront(this.props.Document, true);
             const wid = this.Document[WidthSym]();    // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
             const hgt = this.Document[HeightSym]();
-            this.props.Document[DataSym][this.LayoutFieldKey + "-nativeWidth"] = wid;
-            this.props.Document[DataSym][this.LayoutFieldKey + "-nativeHeight"] = hgt;
+            Doc.SetNativeWidth(this.props.Document[DataSym], wid);
+            Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
         }
     }
 
@@ -907,7 +907,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const excluded = ["PresBox", /* "FormattedTextBox", */ "FontIconBox"]; // bcz: shifting the title for texst causes problems with collaborative use when some people see titles, and others don't
         return !excluded.includes(StrCast(this.layoutDoc.layout));
     }
-    chromeHeight = () => this.showOverlappingTitle ? 1 : 25;
+    chromeHeight = () => this.showOverlappingTitle ? 0 : 25;
 
     @computed get finalLayoutKey() {
         if (typeof this.props.layoutKey === "string") {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index bc38a17eb..88dc3b241 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -76,14 +76,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
 
     componentDidMount() {
         this._pathDisposer = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
-            ({ nativeSize, width }) => {
-                if (this.props.NativeWidth?.() !== 0 || !this.layoutDoc._height) {
-                    this.layoutDoc._nativeWidth = nativeSize.nativeWidth;
-                    this.layoutDoc._nativeHeight = nativeSize.nativeHeight;
-                    this.layoutDoc._nativeOrientation = nativeSize.nativeOrientation;
+            action(({ nativeSize, width }) => {
+                if (!this.layoutDoc._height) {
                     this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
                 }
-            },
+            }),
             { fireImmediately: true });
     }
 
@@ -106,8 +103,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
                 const targetDoc = layoutDoc[DataSym];
                 if (targetDoc[targetField] instanceof ImageField) {
                     this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
-                    this.dataDoc[this.fieldKey + "-nativeWidth"] = NumCast(targetDoc[targetField + "-nativeWidth"]);
-                    this.dataDoc[this.fieldKey + "-nativeHeight"] = NumCast(targetDoc[targetField + "-nativeHeight"]);
+                    Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc));
+                    Doc.SetNativeWidth(this.dataDoc, Doc.NativeHeight(targetDoc));
                     e.stopPropagation();
                 }
             }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 79e00eed7..42b24f6f6 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -50,8 +50,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
     constructor(props: any) {
         super(props);
         this._initialScale = this.props.ScreenToLocalTransform().Scale;
-        const nw = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 927));
-        const nh = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, 1200));
+        const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
+        const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
         !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
         if (pdfUrl) {
@@ -97,8 +97,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
 
     loaded = (nw: number, nh: number, np: number) => {
         this.dataDoc[this.props.fieldKey + "-numPages"] = np;
-        this.dataDoc[this.props.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = Math.max(NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"]), nw * 96 / 72);
-        this.dataDoc[this.props.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = nh * 96 / 72;
+        Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), nw * 96 / 72));
+        Doc.SetNativeHeight(this.dataDoc, nh * 96 / 72);
+        this.layoutDoc._height = this.layoutDoc[WidthSym]() / (Doc.NativeAspect(this.dataDoc) || 1);
         !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
     }
 
@@ -254,7 +255,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
         return <div className={"pdfBox"} onContextMenu={this.specificContextMenu} style={{ height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
             <div className="pdfBox-background"></div>
-            <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
+            <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
                 setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
                 renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
                 addDocTab={this.props.addDocTab} focus={this.props.focus} searchFilterDocs={this.props.searchFilterDocs}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 16ce749bc..f467fef12 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from 'request-promise';
-import { Doc } from "../../../fields/Doc";
+import { Doc, WidthSym } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { InkTool } from "../../../fields/InkField";
 import { listSpec, makeInterface } from "../../../fields/Schema";
@@ -34,12 +34,12 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
 
     videoLoad = () => {
         const aspect = this.player!.videoWidth / this.player!.videoHeight;
-        const nativeWidth = (this.layoutDoc._nativeWidth || 0);
-        const nativeHeight = (this.layoutDoc._nativeHeight || 0);
+        const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+        const nativeHeight = Doc.NativeHeight(this.layoutDoc);
         if (!nativeWidth || !nativeHeight) {
-            if (!this.layoutDoc._nativeWidth) this.layoutDoc._nativeWidth = 400;
-            this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / aspect;
-            this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
+            if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
+            Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
+            this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
         }
     }
 
@@ -48,7 +48,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
         const height = NumCast(this.layoutDoc._height);
         const canvas = document.createElement('canvas');
         canvas.width = 640;
-        canvas.height = 640 * NumCast(this.layoutDoc._nativeHeight) / NumCast(this.layoutDoc._nativeWidth, 1);
+        canvas.height = 640 / (Doc.NativeAspect(this.layoutDoc) || 1);
         const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
         if (ctx) {
             ctx.rect(0, 0, canvas.width, canvas.height);
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 998ddde9a..168c87764 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -6,7 +6,7 @@ import * as rp from 'request-promise';
 import { Doc } from "../../../fields/Doc";
 import { InkTool } from "../../../fields/InkField";
 import { createSchema, makeInterface } from "../../../fields/Schema";
-import { Cast, StrCast } from "../../../fields/Types";
+import { Cast, StrCast, NumCast } from "../../../fields/Types";
 import { VideoField } from "../../../fields/URLField";
 import { Utils, emptyFunction, returnOne, returnZero, OmitKeys } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
@@ -51,8 +51,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
 
     videoLoad = () => {
         const aspect = this.player!.videoWidth / this.player!.videoHeight;
-        this.layoutDoc._nativeWidth = this.player!.videoWidth;
-        this.layoutDoc._nativeHeight = (this.layoutDoc._nativeWidth || 0) / aspect;
+        Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
+        Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
         this.layoutDoc._height = (this.layoutDoc._width || 0) / aspect;
         this.dataDoc[this.fieldKey + "-" + "duration"] = this.player!.duration;
     }
@@ -97,7 +97,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         const height = (this.layoutDoc._height || 0);
         const canvas = document.createElement('canvas');
         canvas.width = 640;
-        canvas.height = 640 * (this.layoutDoc._nativeHeight || 0) / (this.layoutDoc._nativeWidth || 1);
+        canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
         const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
         if (ctx) {
             // ctx.rect(0, 0, canvas.width, canvas.height);
@@ -139,15 +139,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
 
     private createRealSummaryLink = (relative: string) => {
         const url = this.choosePath(Utils.prepend(relative));
-        const width = (this.layoutDoc._width || 0);
-        const height = (this.layoutDoc._height || 0);
+        const width = this.layoutDoc._width || 0;
+        const height = this.layoutDoc._height || 0;
         const imageSummary = Docs.Create.ImageDocument(url, {
-            _nativeWidth: this.layoutDoc._nativeWidth, _nativeHeight: this.layoutDoc._nativeHeight,
+            _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
             x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
             _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-"
         });
-        Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
-        Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
+        Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
+        Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
         imageSummary.isLinkButton = true;
         this.props.addDocument?.(imageSummary);
         DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot");
@@ -164,11 +164,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
 
         if (this.youtubeVideoId) {
             const youtubeaspect = 400 / 315;
-            const nativeWidth = (this.layoutDoc._nativeWidth || 0);
-            const nativeHeight = (this.layoutDoc._nativeHeight || 0);
+            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
             if (!nativeWidth || !nativeHeight) {
-                if (!this.layoutDoc._nativeWidth) this.layoutDoc._nativeWidth = 600;
-                this.layoutDoc._nativeHeight = (this.layoutDoc._nativeWidth || 0) / youtubeaspect;
+                if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
+                Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
                 this.layoutDoc._height = (this.layoutDoc._width || 0) / youtubeaspect;
             }
         }
@@ -346,17 +346,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
         const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
-            onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.layoutDoc._nativeWidth || 640)} height={(this.layoutDoc._nativeHeight || 390)}
+            onLoad={this.youtubeIframeLoaded} className={`${style}`} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
             src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
     }
 
     @action.bound
     addDocumentWithTimestamp(doc: Doc | Doc[]): boolean {
         const docs = doc instanceof Doc ? [doc] : doc;
-        docs.forEach(doc => {
-            const curTime = (this.layoutDoc._currentTimecode || -1);
-            curTime !== -1 && (doc.displayTimecode = curTime);
-        });
+        const curTime = NumCast(this.layoutDoc._currentTimecode);
+        docs.forEach(doc => doc.displayTimecode = curTime);
         return this.addDocument(doc);
     }
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 66dc3cdcc..eb9e08e20 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -72,8 +72,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
     constructor(props: any) {
         super(props);
-        this.dataDoc[this.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 850));
-        this.dataDoc[this.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, this.Document[HeightSym]() / this.Document[WidthSym]() * 850));
+        Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850);
+        Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850);
     }
 
     iframeLoaded = action((e: any) => {
@@ -151,13 +151,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         const field = Cast(this.rootDoc[this.props.fieldKey], WebField);
         if (field?.url.href.indexOf("youtube") !== -1) {
             const youtubeaspect = 400 / 315;
-            const nativeWidth = NumCast(this.layoutDoc._nativeWidth);
-            const nativeHeight = NumCast(this.layoutDoc._nativeHeight);
+            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
             if (field) {
                 if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
-                    if (!nativeWidth) this.layoutDoc._nativeWidth = 600;
-                    this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / youtubeaspect;
-                    this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
+                    if (!nativeWidth) Doc.SetNativeWidth(this.layoutDoc, 600);
+                    Doc.SetNativeHeight(this.layoutDoc, (nativeWidth || 600) / youtubeaspect);
+                    this.layoutDoc._height = this.layoutDoc[WidthSym]() / youtubeaspect;
                 }
             } // else it's an HTMLfield
         } else if (field?.url) {
@@ -388,17 +388,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
-
-    @undoBatch
-    @action
-    toggleNativeDimensions = () => {
-        Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0);
-    }
     specificContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
         funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
-        funcs.push({ description: (this.layoutDoc[this.fieldKey + "-contentWidth"] ? "Unfreeze" : "Freeze") + " Content Width", event: () => this.layoutDoc[this.fieldKey + "-contentWidth"] = this.layoutDoc[this.fieldKey + "-contentWidth"] ? undefined : NumCast(this.layoutDoc._nativeWidth), icon: "snowflake" });
+        funcs.push({ description: (this.layoutDoc[this.fieldKey + "-contentWidth"] ? "Unfreeze" : "Freeze") + " Content Width", event: () => this.layoutDoc[this.fieldKey + "-contentWidth"] = this.layoutDoc[this.fieldKey + "-contentWidth"] ? undefined : Doc.NativeWidth(this.layoutDoc), icon: "snowflake" });
         cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
 
     }
@@ -468,7 +462,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     }
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="webBox-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight) }} ref={this._annotationLayer}>
+        return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) }} ref={this._annotationLayer}>
             {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
                 <Annotation {...this.props} showInfo={emptyFunction} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
             }
@@ -543,9 +537,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             else if (this._mainCont.current) {
                 // set marquee x and y positions to the spatially transformed position
                 const boundingRect = this._mainCont.current.getBoundingClientRect();
-                const boundingHeight = (this.Document._nativeHeight || 1) / (this.Document._nativeWidth || 1) * boundingRect.width;
-                this._startX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1);
-                this._startY = (e.clientY - boundingRect.top) / boundingHeight * (this.Document._nativeHeight || 1);
+                const boundingHeight = (Doc.NativeHeight(this.Document) || 1) / (Doc.NativeWidth(this.Document) || 1) * boundingRect.width;
+                this._startX = (e.clientX - boundingRect.left) / boundingRect.width * (Doc.NativeWidth(this.Document) || 1);
+                this._startY = (e.clientY - boundingRect.top) / boundingHeight * (Doc.NativeHeight(this.Document) || 1);
                 this._marqueeHeight = this._marqueeWidth = 0;
                 this._marqueeing = true;
             }
@@ -560,9 +554,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         if (this._marqueeing && this._mainCont.current) {
             // transform positions and find the width and height to set the marquee to
             const boundingRect = this._mainCont.current.getBoundingClientRect();
-            const boundingHeight = (this.Document._nativeHeight || 1) / (this.Document._nativeWidth || 1) * boundingRect.width;
-            const curX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1);
-            const curY = (e.clientY - boundingRect.top) / boundingHeight * (this.Document._nativeHeight || 1);
+            const boundingHeight = (Doc.NativeHeight(this.Document) || 1) / (Doc.NativeWidth(this.Document) || 1) * boundingRect.width;
+            const curX = (e.clientX - boundingRect.left) / boundingRect.width * (Doc.NativeWidth(this.Document) || 1);
+            const curY = (e.clientY - boundingRect.top) / boundingHeight * (Doc.NativeHeight(this.Document) || 1);
             this._marqueeWidth = curX - this._startX;
             this._marqueeHeight = curY - this._startY;
             this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth);
@@ -622,7 +616,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     visibleHeiht = () => {
         if (this._mainCont.current) {
             const boundingRect = this._mainCont.current.getBoundingClientRect();
-            const scalin = (this.Document._nativeWidth || 0) / boundingRect.width;
+            const scalin = (Doc.NativeWidth(this.Document) || 0) / boundingRect.width;
             return Math.min(boundingRect.height * scalin, this.props.PanelHeight() * scalin);
         }
         return this.props.PanelHeight();
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 123946dea..7cd92b8b9 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -82,7 +82,7 @@ export class DashDocView extends React.Component<IDashDocView> {
         const { scale, translateX, translateY } = Utils.GetScreenTransform(outerElement);
         return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale);
     }
-    contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
+    contentScaling = () => Doc.NativeWidth(this._dashDoc) > 0 ? this._dashDoc![WidthSym]() / Doc.NativeWidth(this._dashDoc) : 1;
 
     outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document);  // ideally, this would scroll to show the focus target
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 0d92d7062..9307f1649 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -23,7 +23,6 @@
     overflow-y: auto;
     overflow-x: hidden;
     color: initial;
-    max-height: 100%;
     display: flex;
     flex-direction: row;
     transition: opacity 1s;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 903bbaaa3..d44f35a96 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -663,7 +663,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const optionItems = options && "subitems" in options ? options.subitems : [];
         !Doc.UserDoc().noviceMode && optionItems.push({ description: !this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
         optionItems.push({ description: `${this.Document._autoHeight ? "Lock" : "Auto"} Height`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-        optionItems.push({ description: `${!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" });
+        optionItems.push({ description: `${!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" });
         !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
         this._downX = this._downY = Number.NaN;
     }
@@ -1454,10 +1454,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     public endUndoTypingBatch() {
         const wasUndoing = this._undoTyping;
-        if (this._undoTyping) {
-            this._undoTyping.end();
-            this._undoTyping = undefined;
-        }
+        this._undoTyping?.end();
+        this._undoTyping = undefined;
         return wasUndoing;
     }
     public static LiveTextUndo: UndoManager.Batch | undefined;
@@ -1470,8 +1468,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
         FormattedTextBox.LiveTextUndo?.end();
         FormattedTextBox.LiveTextUndo = undefined;
-        // move the richtextmenu offscreen
-        //if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
     }
 
     _lastTimedMark: Mark | undefined = undefined;
@@ -1574,10 +1570,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             />;
     }
 
+    sidebarContentScaling = () => this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
     @computed get sidebarCollection() {
         const fitToBox = this.props.Document._fitToBox;
         const collectionProps = {
             ...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit,
+            NativeWidth: returnZero,
+            NativeHeight: returnZero,
             PanelHeight: this.props.PanelHeight,
             PanelWidth: this.sidebarWidth,
             xMargin: 0,
@@ -1591,7 +1590,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             isSelected: this.props.isSelected,
             select: emptyFunction,
             active: this.annotationsActive,
-            ContentScaling: returnOne,
+            ContentScaling: this.sidebarContentScaling,
             whenActiveChanged: this.whenActiveChanged,
             removeDocument: this.removeDocument,
             moveDocument: this.moveDocument,
@@ -1626,7 +1625,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
         const selclass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
         return (
-            <div className={"formattedTextBox-cont"} ref={this._boxRef}
+            <div className="formattedTextBox-cont" ref={this._boxRef}
                 style={{
                     transform: `scale(${scale})`,
                     transformOrigin: "top left",
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 32038d1ee..3e5a40084 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -320,8 +320,8 @@ export class FormattedTextBoxComment {
                                             whenActiveChanged={returnFalse}
                                             bringToFront={returnFalse}
                                             ContentScaling={returnOne}
-                                            NativeWidth={target._nativeWidth ? (() => NumCast(target._nativeWidth)) : undefined}
-                                            NativeHeight={target._natvieHeight ? (() => NumCast(target._nativeHeight)) : undefined}
+                                            NativeWidth={Doc.NativeWidth(target) ? (() => Doc.NativeWidth(target)) : undefined}
+                                            NativeHeight={Doc.NativeHeight(target) ? (() => Doc.NativeHeight(target)) : undefined}
                                         />
                                     </div>
                                 </div>;
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 1767a04de..40c1d1cac 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -31,7 +31,7 @@ export class DashDocView {
     }
 
     //moved
-    contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
+    contentScaling = () => Doc.NativeWidth(this._dashDoc) > 0 ? this._dashDoc![WidthSym]() / Doc.NativeWidth(this._dashDoc) : 1;
 
     //moved
     outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document);  // ideally, this would scroll to show the focus target
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a8a6e8c33..d278612b1 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -73,7 +73,7 @@ interface IViewerProps {
     renderDepth: number;
     focus: (doc: Doc) => void;
     isSelected: (outsideReaction?: boolean) => boolean;
-    loaded: (nw: number, nh: number, np: number) => void;
+    loaded?: (nw: number, nh: number, np: number) => void;
     active: (outsideReaction?: boolean) => boolean;
     isChildActive: (outsideReaction?: boolean) => boolean;
     addDocTab: (document: Doc, where: string) => boolean;
@@ -238,7 +238,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                         width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]),
                         height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0])
                     });
-                    i === this.props.pdf.numPages - 1 && this.props.loaded((page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]),
+                    i === this.props.pdf.numPages - 1 && this.props.loaded?.((page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]),
                         (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]), i);
                 }))));
             this.Document.scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72;
@@ -681,14 +681,14 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
 
 
     getCoverImage = () => {
-        if (!this.props.Document[HeightSym]() || !this.props.Document._nativeHeight) {
+        if (!this.props.Document[HeightSym]() || !Doc.NativeHeight(this.props.Document)) {
             setTimeout((() => {
                 this.Document._height = this.Document[WidthSym]() * this._coverPath.height / this._coverPath.width;
-                this.Document._nativeHeight = (this.Document._nativeWidth || 0) * this._coverPath.height / this._coverPath.width;
+                Doc.SetNativeWidth(this.Document, (Doc.NativeWidth(this.Document) || 0) * this._coverPath.height / this._coverPath.width);
             }).bind(this), 0);
         }
-        const nativeWidth = (this.Document._nativeWidth || 0);
-        const nativeHeight = (this.Document._nativeHeight || 0);
+        const nativeWidth = Doc.NativeWidth(this.Document);
+        const nativeHeight = Doc.NativeHeight(this.Document);
         const resolved = Utils.prepend(this._coverPath.path);
         return <img key={resolved} src={resolved} onError={action(() => this._coverPath.path = "http://www.cs.brown.edu/~bcz/face.gif")} onLoad={action(() => this._showWaiting = false)}
             style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />;
@@ -708,7 +708,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
 
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="pdfViewerDash-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
+        return <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.Document), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
             {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
                 <Annotation {...this.props} showInfo={this.showInfo} select={this.props.select} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
             }
@@ -726,8 +726,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
 
     showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
     overlayTransform = () => this.scrollXf().scale(1 / this._zoomed);
-    panelWidth = () => (this.Document.scrollHeight || this.Document._nativeHeight || 0);
-    panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
+    panelWidth = () => (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     @computed get overlayLayer() {
         return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
             style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, transform: `scale(${this._zoomed})` }}>
@@ -783,8 +783,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
             onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
             style={{
                 overflowX: this._zoomed !== 1 ? "scroll" : undefined,
-                width: !this.props.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.props.Document._nativeWidth) : `${100 / this.contentScaling}%`,
-                height: !this.props.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.props.Document._nativeHeight) : `${100 / this.contentScaling}%`,
+                width: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeWidth(this.props.Document) : `${100 / this.contentScaling}%`,
+                height: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`,
                 transform: `scale(${this.props.ContentScaling()})`
             }}  >
             {this.pdfViewerDiv}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3872cbb18..6cfd1f9d0 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -354,7 +354,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             doc.x = x;
             doc.y = y;
             const size = 200;
-            const aspect = NumCast(doc._nativeHeight) / NumCast(doc._nativeWidth, 1);
+            const aspect = Doc.NativeHeight(doc) / (Doc.NativeWidth(doc) || 1);
             if (aspect > 1) {
                 doc._height = size;
                 doc._width = size / aspect;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 1a062fa3b..adda40621 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -25,6 +25,7 @@ import JSZip = require("jszip");
 import { saveAs } from "file-saver";
 import { CollectionDockingView } from "../client/views/collections/CollectionDockingView";
 import { SelectionManager } from "../client/util/SelectionManager";
+import { DocumentView } from "../client/views/nodes/DocumentView";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -882,6 +883,15 @@ export namespace Doc {
     export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; }
     export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; }
     export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; }
+    export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
+        return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
+    }
+    export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) { return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeWidth"], useWidth ? doc[WidthSym]() : 0)); }
+    export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) { return !doc ? 0 : NumCast(doc._nativeHeight, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeHeight"], useHeight ? doc[HeightSym]() : 0)); }
+    export function SetNativeWidth(doc: Doc, width: number | undefined) { doc[Doc.LayoutFieldKey(doc) + "-nativeWidth"] = width; }
+    export function SetNativeHeight(doc: Doc, height: number | undefined) { doc[Doc.LayoutFieldKey(doc) + "-nativeHeight"] = height; }
+
+
     const manager = new DocData();
     export function SearchQuery(): string { return manager._searchQuery; }
     export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); }
@@ -1086,14 +1096,14 @@ export namespace Doc {
 
     export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
         runInAction(() => {
-            if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) {
+            if (Doc.NativeWidth(layoutDoc) || Doc.NativeHeight(layoutDoc)) {
                 layoutDoc._viewScale = NumCast(layoutDoc._viewScale, 1) * contentScale;
                 layoutDoc._nativeWidth = undefined;
                 layoutDoc._nativeHeight = undefined;
             }
             else {
                 layoutDoc._autoHeight = false;
-                if (!layoutDoc._nativeWidth) {
+                if (!Doc.NativeWidth(layoutDoc)) {
                     layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
                     layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
                 }
-- 
cgit v1.2.3-70-g09d2


From 37fc6beb4662be195b8aa551cc1042b04f2c24a8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 17 Oct 2020 22:01:53 -0400
Subject: fixed rubberbadning ink shapes not to get cutoff. fixed animating
 annotations.   fixed ink masks. fixed warnings.

---
 src/client/views/InkingStroke.tsx                            |  4 +---
 src/client/views/collections/CollectionMenu.tsx              |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  6 ++++--
 src/client/views/nodes/PresBox.tsx                           | 12 ++++++------
 src/client/views/pdf/PDFViewer.tsx                           |  6 +++++-
 src/server/websocket.ts                                      |  4 ++--
 6 files changed, 19 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 186406424..7e424d8f0 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -38,7 +38,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         this.props.Document._backgroundColor = "rgba(0,0,0,0.7)";
         this.props.Document.mixBlendMode = "hard-light";
         this.props.Document.color = "#9b9b9bff";
-        this.props.Document._stayInCollection = true;
+        //this.props.Document._stayInCollection = true;
         this.props.Document.isInkMask = true;
     }
 
@@ -190,8 +190,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
 
         return (
             <svg className="inkingStroke"
-                width={Math.max(width, height)}
-                height={Math.max(width, height)}
                 style={{
                     pointerEvents: this.props.Document.isInkMask ? "all" : "none",
                     transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index d08b31340..a4f5118d6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -533,7 +533,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
     }
     get document() { return this.props.docView.props.Document; }
     @computed get dataField() {
-        return this.document[this.props.docView.LayoutFieldKey];
+        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
     }
     @computed get childDocs() {
         return DocListCast(this.dataField);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c863addd2..56624f42d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -202,13 +202,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
         if (!super.onInternalDrop(e, de)) return false;
+        const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getTransformOverlay().transformPoint(de.x, de.y);
-        const z = NumCast(docDragData.droppedDocuments[0].z);
+        const z = NumCast(refDoc.z);
         const x = (z ? xpo : xp) - docDragData.offset[0];
         const y = (z ? ypo : yp) - docDragData.offset[1];
         const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
         zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1);
-        const dropPos = [NumCast(docDragData.droppedDocuments[0].x), NumCast(docDragData.droppedDocuments[0].y)];
+        const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
+        const dropPos = this.Document._currentFrame !== undefined ? [dvals.x, dvals.y] : [NumCast(refDoc.x), NumCast(refDoc.y)];
         for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
             const d = docDragData.droppedDocuments[i];
             const layoutDoc = Doc.Layout(d);
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 3b7794815..01a7bed8c 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -288,7 +288,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const willZoom = false;
         const openInTab = () => {
             collectionDocView ? collectionDocView.props.addDocTab(activeItem, "replace") : this.props.addDocTab(activeItem, "replace:left");
-        }
+        };
         // If openDocument is selected then it should open the document for the user
         if (activeItem.openDocument) {
             openInTab();
@@ -299,10 +299,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             } else if (docToJump === curDoc) {
                 //checking if curDoc has navigation open
                 if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
-                    await DocumentManager.Instance.jumpToDocument(targetDoc, false, () => { openInTab() }, srcContext); // documents open in new tab instead of on right
+                    await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext); // documents open in new tab instead of on right
                 } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
                     //awaiting jump so that new scale can be found, since jumping is async
-                    await DocumentManager.Instance.jumpToDocument(targetDoc, true, () => { openInTab() }, srcContext); // documents open in new tab instead of on right
+                    await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext); // documents open in new tab instead of on right
                 }
             } else {
                 //awaiting jump so that new scale can be found, since jumping is async
@@ -442,7 +442,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
                 }
             }
         };
-        this.layoutDoc.presStatus = PresStatus.Auto;
+        this.layoutDoc.presStatus = PresStatus.Autoplay;
         this.startPresentation(startSlide);
         this.gotoDocument(startSlide, this.itemIndex);
         load();
@@ -450,7 +450,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
 
     @action
     pauseAutoPres = () => {
-        if (this.layoutDoc.presStatus === PresStatus.Auto) {
+        if (this.layoutDoc.presStatus === PresStatus.Autoplay) {
             if (this._presTimer) clearTimeout(this._presTimer);
             this.layoutDoc.presStatus = PresStatus.Manual;
             this.layoutDoc.presLoop = false;
@@ -611,7 +611,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         return true;
     }
     childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
-    removeDocument = (doc: Doc) => { return Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); };
+    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
     active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground) &&
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index d278612b1..76b218972 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -730,7 +730,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     @computed get overlayLayer() {
         return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
-            style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, transform: `scale(${this._zoomed})` }}>
+            style={{
+                pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined,
+                mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
+                transform: `scale(${this._zoomed})`
+            }}>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                 LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
                 annotationsKey={this.annotationKey}
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 490760441..6ceb9e29f 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -278,8 +278,8 @@ export namespace WebSocket {
         diff.diff.$set = diff.diff.$addToSet; delete diff.diff.$addToSet;// convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
         const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
         const newListItems = diff.diff.$set[updatefield].fields;
-        const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields || [];
-        diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
+        const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((item: any) => item !== undefined) || [];
+        diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => newItem && !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
         const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
         delete diff.diff.length;
         Database.Instance.update(diff.id, diff.diff,
-- 
cgit v1.2.3-70-g09d2

