From bbc993dd70d105c3de078208763e6415eedb1ff7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Nov 2022 11:18:46 -0500
Subject: partial fix for previewing multiple links to different sidebar notes.
  fixed opening sidebar to create an annotation the first time.   fixed
 honoring dashFieldView's non-editable flag.  moved isLinkButton setter to
 expert and make isLinkButton default for Buttons.  fixed following Links with
 Zoom to reset view.  fixed view for editing onClick for documents

---
 src/client/views/DocumentButtonBar.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ce77b7446..3f12c7c9d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -356,7 +356,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <Tooltip title={<div className="dash-tooltip">{'Click to record 5 second annotation'}</div>}>
                 <div
                     className="documentButtonBar-icon"
-                    style={{ backgroundColor: this._isRecording ? 'red' : targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
+                    style={{ backgroundColor: this._isRecording ? Colors.ERROR_RED : Colors.DARK_GRAY, color: Colors.WHITE }}
                     onClick={undoBatch(
                         action(e => {
                             this._isRecording = true;
@@ -463,7 +463,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         {this.metadataButton}
                     </div> */
                 }
-                {!SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
+                {Doc.noviceMode || !SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
                 <div className="documentButtonBar-button">{this.pinButton}</div>
                 {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
                 {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : (
-- 
cgit v1.2.3-70-g09d2


From 7bde06fc756684d47c89c65199492daef5fe5b63 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Nov 2022 09:51:49 -0500
Subject: made audio anno recording last as long as record button is pressed.
 added a Hide option to allow an pres item to be animated but not visible
 (used to setup state for subsequent activity))

---
 src/client/views/DocumentButtonBar.tsx    | 36 ++++++++++---------
 src/client/views/nodes/DocumentView.tsx   | 10 +++---
 src/client/views/nodes/trails/PresBox.tsx | 60 ++++++++++++++++++-------------
 3 files changed, 61 insertions(+), 45 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 3f12c7c9d..a110bf51a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
 import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
 import { Cast, NumCast } from '../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
+import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { Docs } from '../documents/Documents';
@@ -14,7 +14,7 @@ import { DragManager } from '../util/DragManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
-import { undoBatch } from '../util/UndoManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { TabDocView } from './collections/TabDocView';
 import './DocumentButtonBar.scss';
@@ -349,28 +349,30 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     }
 
     @observable _isRecording = false;
+    _stopFunc: () => void = emptyFunction;
     @computed
     get recordButton() {
         const targetDoc = this.view0?.props.Document;
         return !targetDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">{'Click to record 5 second annotation'}</div>}>
+            <Tooltip title={<div className="dash-tooltip">Press to record audio annotation</div>}>
                 <div
                     className="documentButtonBar-icon"
                     style={{ backgroundColor: this._isRecording ? Colors.ERROR_RED : Colors.DARK_GRAY, color: Colors.WHITE }}
-                    onClick={undoBatch(
-                        action(e => {
-                            this._isRecording = true;
-                            this.props.views().map(
-                                view =>
-                                    view &&
-                                    DocumentViewInternal.recordAudioAnnotation(
-                                        view.dataDoc,
-                                        view.LayoutFieldKey,
-                                        action(() => (this._isRecording = false))
-                                    )
-                            );
-                        })
-                    )}>
+                    onPointerDown={action((e: React.PointerEvent) => {
+                        this._isRecording = true;
+                        this.props.views().map(
+                            view =>
+                                view &&
+                                DocumentViewInternal.recordAudioAnnotation(
+                                    view.dataDoc,
+                                    view.LayoutFieldKey,
+                                    stopFunc => (this._stopFunc = stopFunc),
+                                    action(() => (this._isRecording = false))
+                                )
+                        );
+                        const b = UndoManager.StartBatch('Recording');
+                        setupMoveUpEvents(this, e, returnFalse, () => this._stopFunc(), emptyFunction);
+                    })}>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="microphone" />
                 </div>
             </Tooltip>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6b96362bf..c669d2b96 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1208,7 +1208,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
     };
 
-    static recordAudioAnnotation(dataDoc: Doc, field: string, onEnd?: () => void) {
+    static recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
         let gumStream: any;
         let recorder: any;
         navigator.mediaDevices
@@ -1245,12 +1245,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 };
                 runInAction(() => (dataDoc.audioAnnoState = 'recording'));
                 recorder.start();
-                setTimeout(() => {
+                const stopFunc = () => {
                     recorder.stop();
                     DictationManager.Controls.stop(false);
                     runInAction(() => (dataDoc.audioAnnoState = 'stopped'));
                     gumStream.getAudioTracks()[0].stop();
-                }, 5000);
+                };
+                if (onRecording) onRecording(stopFunc);
+                else setTimeout(stopFunc, 5000);
             });
     }
 
@@ -1385,7 +1387,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 : this.boxShadow || (this.props.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
         const renderDoc = this.renderDoc({
             borderRadius: this.borderRounding,
-            outline: highlighting && !this.borderRounding && !highlighting.highlightStroke? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px',
+            outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px',
             border: highlighting && this.borderRounding && highlighting.highlightStyle === 'dashed' ? `${highlighting.highlightStyle} ${highlighting.highlightColor} ${highlighting.highlightIndex}px` : undefined,
             boxShadow,
             clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined,
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 5e9301e6a..9e3b42cf6 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -330,7 +330,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 this.startTempMedia(targetDoc, activeItem);
             }
             if (targetDoc) {
-                Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc);
+                //  Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc);
                 targetDoc && runInAction(() => (targetDoc.focusSpeed = activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500)));
                 setTimeout(() => (targetDoc.focusSpeed = undefined), NumCast(targetDoc.focusSpeed) + 10);
             }
@@ -392,12 +392,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 () =>
                     StrListCast(activeItem.presPinLayoutData)
                         .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
-                        .forEach(
-                            action(data => {
-                                const doc = DocServer.GetCachedRefField(data.id) as Doc;
-                                doc._dataTransition = undefined;
-                            })
-                        ),
+                        .forEach(action(data => ((DocServer.GetCachedRefField(data.id) as Doc)._dataTransition = undefined))),
                 transTime + 10
             );
         }
@@ -616,26 +611,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (tagDoc === this.layoutDoc.presCollection) {
                 tagDoc.opacity = 1;
             } else {
-                if (curDoc.presHideBefore) {
-                    if (itemIndexes.length > 1 && curInd !== 0) {
+                const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
+                if (curDoc.presHideBefore && index === hidingIndBef) {
+                    if (index > this.itemIndex) {
+                        tagDoc.opacity = 0;
+                    } else if (index === this.itemIndex || !curDoc.presHideAfter) {
                         tagDoc.opacity = 1;
-                    } else {
-                        if (index > this.itemIndex) {
-                            tagDoc.opacity = 0;
-                        } else if (index === this.itemIndex || !curDoc.presHideAfter) {
-                            tagDoc.opacity = 1;
-                        }
                     }
                 }
-                if (curDoc.presHideAfter) {
-                    if (itemIndexes.length > 1 && curInd !== itemIndexes.length - 1) {
+                const hidingIndAft = itemIndexes
+                    .slice()
+                    .reverse()
+                    .find(item => item < this.itemIndex);
+                if (curDoc.presHideAfter && index === hidingIndAft) {
+                    if (index < this.itemIndex) {
+                        tagDoc.opacity = 0;
+                    } else if (index === this.itemIndex || !curDoc.presHideBefore) {
                         tagDoc.opacity = 1;
-                    } else {
-                        if (index < this.itemIndex) {
-                            tagDoc.opacity = 0;
-                        } else if (index === this.itemIndex || !curDoc.presHideBefore) {
-                            tagDoc.opacity = 1;
-                        }
+                    }
+                }
+                const hidingInd = itemIndexes.find(item => item === this.itemIndex);
+                if (curDoc.presHide && index === hidingInd) {
+                    if (index === this.itemIndex) {
+                        tagDoc.opacity = 0;
                     }
                 }
             }
@@ -1180,6 +1178,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
     };
 
+    @undoBatch
+    @action
+    updateHide = (activeItem: Doc) => {
+        activeItem.presHide = !activeItem.presHide;
+        this.selectedArray.forEach(doc => (doc.presHide = activeItem.presHide));
+    };
+
     @undoBatch
     @action
     updateHideAfter = (activeItem: Doc) => {
@@ -1317,7 +1322,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </div>
                             </div>
                         </div>
-                        {inputter('0.1', '0.1', '10', transitionSpeed, [PresMovement.Pan, PresMovement.Zoom].includes(activeItem.presMovement as any), this.setTransitionTime)}
+                        {inputter('0.1', '0.1', '10', transitionSpeed, true, this.setTransitionTime)}
                         <div className={'slider-headers'}>
                             <div className="slider-text">Fast</div>
                             <div className="slider-text">Medium</div>
@@ -1334,6 +1339,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     </div>
                                 </Tooltip>
                             )}
+                            {isPresCollection ? null : (
+                                <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
+                                    <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+                                        Hide
+                                    </div>
+                                </Tooltip>
+                            )}
                             {isPresCollection ? null : (
                                 <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
                                     <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
-- 
cgit v1.2.3-70-g09d2


From 3d8bd17fa4f031f2223840b336c313a18e7e87aa Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Nov 2022 14:29:48 -0500
Subject: fixed following link 'inPlace' from within lightbox to do same thing
 as if out of lightbox.  fixed contentActive behavior for multicolumn view. 
 removed progressivize from presbox.

---
 src/client/documents/Documents.ts                  |   1 -
 src/client/util/DocumentManager.ts                 |  24 +-
 src/client/util/LinkFollower.ts                    |   7 +-
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/LightboxView.tsx                  |   8 +-
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/PropertiesView.tsx                |  14 -
 src/client/views/collections/TabDocView.tsx        |   9 +-
 .../collectionFreeForm/CollectionFreeFormView.scss |  50 ---
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  54 +--
 .../CollectionMulticolumnView.tsx                  |  10 +-
 .../CollectionMultirowView.tsx                     |   8 +-
 src/client/views/nodes/DocumentView.scss           |   6 +-
 src/client/views/nodes/trails/PresBox.tsx          | 475 +--------------------
 14 files changed, 75 insertions(+), 595 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f1b8c3034..e44004f45 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -259,7 +259,6 @@ export class DocumentOptions {
     appearFrame?: number; // the frame in which the document appears
     presTransition?: number; //the time taken for the transition TO a document
     presDuration?: number; //the duration of the slide in presentation view
-    presProgressivize?: boolean;
     borderRounding?: string;
     boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
     data?: any;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index b046d950f..01ca24439 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -131,6 +131,7 @@ export class DocumentManager {
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
     };
     public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
+        if (LightboxView.LightboxDoc) return DocumentManager.Instance.getLightboxDocumentView(toFind, originatingDoc);
         const views = this.getDocumentViews(toFind).filter(view => view.rootDoc !== originatingDoc);
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
     };
@@ -168,8 +169,7 @@ export class DocumentManager {
         presZoomScale?: number
     ): void => {
         originalTarget = originalTarget ?? targetDoc;
-        const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-        const docView = getFirstDocView(targetDoc, originatingDoc);
+        const docView = this.getFirstDocumentView(targetDoc, originatingDoc);
         const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
         const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? docView?.rootDoc ?? targetDoc : docView?.rootDoc ?? targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field
         var wasHidden = resolvedTarget.hidden;
@@ -206,11 +206,11 @@ export class DocumentManager {
             }
             finished?.();
         };
-        const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc);
+        const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && this.getFirstDocumentView(annotatedDoc);
         const contextDocs = docContext.length ? DocListCast(docContext[0].data) : undefined;
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
-        const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
+        const targetDocContextView = (targetDocContext && this.getFirstDocumentView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (annoContainerView) {
             if (annoContainerView.props.Document.layoutKey === 'layout_icon') {
@@ -281,7 +281,7 @@ export class DocumentManager {
                     } else {
                         // no timecode means we need to find the context view and focus on our target
                         const findView = (delay: number) => {
-                            const retryDocView = getFirstDocView(resolvedTarget); // test again for the target view snce we presumably created the context above by focusing on it
+                            const retryDocView = this.getFirstDocumentView(resolvedTarget); // test again for the target view snce we presumably created the context above by focusing on it
                             if (retryDocView) {
                                 // we found the target in the context.
                                 Doc.linkFollowHighlight(retryDocView.rootDoc);
@@ -316,10 +316,20 @@ export class DocumentManager {
                         }
                     }
                     // there's no context view so we need to create one first and try again when that finishes
-                    const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget);
                     createViewFunc(
                         targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
-                        finishFunc
+                        () =>
+                            this.jumpToDocument(
+                                targetDoc,
+                                true,
+                                (doc: Doc, finished?: () => void) => doc !== targetDocContext && createViewFunc(doc, finished),
+                                docContext,
+                                linkDoc,
+                                true /* if target not found, get rid of context just created */,
+                                undefined,
+                                finished,
+                                originalTarget
+                            )
                     );
                 }
             }
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 1608a77f2..c09c9d1c5 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -30,11 +30,10 @@ export class LinkFollower {
         const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
             const createTabForTarget = (didFocus: boolean) =>
                 new Promise<ViewAdjustment>(res => {
-                    const where = LightboxView.LightboxDoc ? 'lightbox' : StrCast(sourceDoc.followLinkLocation, followLoc);
+                    const where = LightboxView.LightboxDoc ? 'inPlace' : StrCast(sourceDoc.followLinkLocation, followLoc);
                     docViewProps.addDocTab(doc, where);
                     setTimeout(() => {
-                        const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-                        const targDocView = getFirstDocView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
+                        const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
                         if (targDocView) {
                             targDocView.props.focus(doc, {
                                 willZoom: BoolCast(sourceDoc.followLinkZoom, false),
@@ -110,7 +109,7 @@ export class LinkFollower {
                             containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
                         }
                         const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'lightbox'), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
+                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'inPlace'), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
                     }
                 } else {
                     allFinished();
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index a110bf51a..36875290e 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -458,7 +458,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
                     </div>
                 ) : null}
-                {Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.recordButton}</div>}
                 {
                     Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.templateButton}</div>
                     /*<div className="documentButtonBar-button">
@@ -467,6 +466,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                 }
                 {Doc.noviceMode || !SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
                 <div className="documentButtonBar-button">{this.pinButton}</div>
+                <div className="documentButtonBar-button">{this.recordButton}</div>
                 {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
                 {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : (
                     <div className="documentButtonBar-button" style={{ display: !considerPush ? 'none' : '' }}>
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 91773419a..5660a34e9 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -4,7 +4,8 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { InkTool } from '../../fields/InkField';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { List } from '../../fields/List';
+import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
@@ -141,6 +142,11 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         }
     }
     public static AddDocTab = (doc: Doc, location: string, layoutTemplate?: Doc, openInTabFunc?: any) => {
+        const inPlaceView = DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
+        if (inPlaceView) {
+            inPlaceView.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List<Doc>([doc]);
+            return true;
+        }
         LightboxView.openInTabFunc = openInTabFunc;
         SelectionManager.DeselectAll();
         return LightboxView.SetLightboxDoc(
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9648a7807..987bfc23d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -690,11 +690,11 @@ export class MainView extends React.Component {
         switch (locationFields[0]) {
             default:
             case 'inPlace':
+            case 'lightbox':   return LightboxView.AddDocTab(doc, location);
             case 'add':        return CollectionDockingView.AddSplit(doc, locationParams);
             case 'dashboard':  return DashboardView.openDashboard(doc);
             case 'close':      return CollectionDockingView.CloseSplit(doc, locationParams);
             case 'fullScreen': return CollectionDockingView.OpenFullScreen(doc);
-            case 'lightbox':   return LightboxView.AddDocTab(doc, location);
             case 'toggle':     return CollectionDockingView.ToggleSplit(doc, locationParams);
         }
     };
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 842664402..1f161c01b 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -95,7 +95,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     //Pres Trails booleans:
     @observable openPresTransitions: boolean = false;
-    @observable openPresProgressivize: boolean = false;
     @observable openAddSlide: boolean = false;
     @observable openSlideOptions: boolean = false;
 
@@ -1651,19 +1650,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                 {this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
                             </div>
                         )}
-                        {/* {!selectedItem || type === DocumentType.VID || type === DocumentType.AUDIO ? (null) : <div className="propertiesView-presTrails">
-                        <div className="propertiesView-presTrails-title"
-                            onPointerDown={action(() => this.openPresProgressivize = !this.openPresProgressivize)}
-                            style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>
-                            &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={"tasks"} /> &nbsp; Progressivize
-                        <div className="propertiesView-presTrails-title-icon">
-                                <FontAwesomeIcon icon={this.openPresProgressivize ? "caret-down" : "caret-right"} size="lg" color="white" />
-                            </div>
-                        </div>
-                        {this.openPresProgressivize ? <div className="propertiesView-presTrails-content">
-                            {PresBox.Instance.progressivizeDropdown}
-                        </div> : null}
-                    </div>} */}
                         {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
                             <div className="propertiesView-presTrails">
                                 <div
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e0c422f94..1a9006356 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -11,7 +11,7 @@ import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { DocUtils } from '../../documents/Documents';
@@ -360,11 +360,16 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             //     // TabDocView.PinDoc(doc, { hidePresBox: true });
             //     return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab);
             // }
+            case 'inPlace':
+                const inPlaceView = DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
+                if (inPlaceView) {
+                    inPlaceView.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List<Doc>([doc]);
+                    return true;
+                }
             case 'lightbox':
                 return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab);
             case 'toggle':
                 return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
-            case 'inPlace':
             case 'add':
             default:
                 return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index d80fcdfc3..7a7ae3f40 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -96,37 +96,6 @@
     border-color: #69a5db;
 }
 
-.progressivizeButton {
-    position: absolute;
-    display: grid;
-    grid-template-columns: auto 20px auto;
-    transform: translate(-105%, 0);
-    align-items: center;
-    border: black solid 1px;
-    border-radius: 3px;
-    justify-content: center;
-    width: 40;
-    z-index: 30000;
-    height: 20;
-    overflow: hidden;
-    background-color: #d5dce2;
-    transition: all 1s;
-
-    .progressivizeButton-prev:hover {
-        color: #5a9edd;
-    }
-
-    .progressivizeButton-frame {
-        justify-self: center;
-        text-align: center;
-        width: 15px;
-    }
-
-    .progressivizeButton-next:hover {
-        color: #5a9edd;
-    }
-}
-
 .resizable {
     background: rgba(0, 0, 0, 0.2);
     width: 100px;
@@ -178,25 +147,6 @@
     }
 }
 
-.progressivizeMove-frame {
-    width: 20px;
-    border-radius: 2px;
-    z-index: 100000;
-    color: white;
-    text-align: center;
-    background-color: #5a9edd;
-    transform: translate(-110%, 110%);
-}
-
-.progressivizeButton:hover {
-    box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.5);
-
-    .progressivizeButton-frame {
-        background-color: #5a9edd;
-        color: white;
-    }
-}
-
 .collectionFreeform-customText {
     position: absolute;
     text-align: center;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d44c2f160..e24b116d0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,5 +1,5 @@
 import { Bezier } from 'bezier-js';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { DateField } from '../../../../fields/DateField';
@@ -71,6 +71,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return 'CollectionFreeFormView(' + this.props.Document.title?.toString() + ')';
     } // this makes mobx trace() statements more descriptive
 
+    @observable
+    public static ShowPresPaths = false;
+
     private _lastNudge: any;
     private _lastX: number = 0;
     private _lastY: number = 0;
@@ -1866,6 +1869,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         );
     }
 
+    showPresPaths = () => (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null);
+
     @computed get marqueeView() {
         TraceMobx();
         return (
@@ -1912,8 +1917,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
                         transform={this.contentTransform}
                         zoomScaling={this.zoomScaling}
-                        presPaths={BoolCast(this.Document.presPathView)}
-                        progressivize={BoolCast(this.Document.editProgressivize)}
+                        presPaths={this.showPresPaths}
                         presPinView={BoolCast(this.Document.presPinView)}
                         transition={this._viewTransition ? `transform ${this._viewTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', null)}
                         viewDefDivClick={this.props.viewDefDivClick}>
@@ -2039,8 +2043,7 @@ interface CollectionFreeFormViewPannableContentsProps {
     viewDefDivClick?: ScriptField;
     children: () => JSX.Element[];
     transition?: string;
-    presPaths?: boolean;
-    progressivize?: boolean;
+    presPaths: () => JSX.Element | null;
     presPinView?: boolean;
     isAnnotationOverlay: boolean | undefined;
     isAnnotationOverlayScrollable: boolean | undefined;
@@ -2093,42 +2096,11 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
         return true;
     };
 
-    // scale: NumCast(targetDoc._viewScale),
-    @computed get zoomProgressivizeContainer() {
-        const activeItem = PresBox.Instance.activeItem;
-        // const targetDoc = PresBox.Instance.targetDoc;
-        if (activeItem && activeItem.presPinView && activeItem.id) {
-            const left = NumCast(activeItem.presPinViewX);
-            const top = NumCast(activeItem.presPinViewY);
-            const width = 100;
-            const height = 100;
-            return !this.props.presPinView ? null : (
-                <div key="resizable" className="resizable" onPointerDown={this.onPointerDown} style={{ width, height, top, left, position: 'absolute' }}>
-                    <div className="resizers" key={'resizer' + activeItem.id}>
-                        <div className="resizer top-left" onPointerDown={this.onPointerDown} />
-                        <div className="resizer top-right" onPointerDown={this.onPointerDown} />
-                        <div className="resizer bottom-left" onPointerDown={this.onPointerDown} />
-                        <div className="resizer bottom-right" onPointerDown={this.onPointerDown} />
-                    </div>
-                </div>
-            );
-        }
-    }
-
-    @computed get zoomProgressivize() {
-        return PresBox.Instance?.activeItem?.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : null;
-    }
-
-    @computed get progressivize() {
-        return PresBox.Instance && this.props.progressivize ? PresBox.Instance.progressivizeChildDocs : null;
-    }
-
     @computed get presPaths() {
-        const presPaths = 'presPaths' + (this.props.presPaths ? '' : '-hidden');
-        return !PresBox.Instance || !this.props.presPaths ? null : (
+        return !this.props.presPaths() ? null : (
             <>
-                <div key="presorder">{PresBox.Instance.order}</div>
-                <svg key="svg" className={presPaths}>
+                <div key="presorder">{PresBox.Instance?.order}</div>
+                <svg key="svg" className="presPaths">
                     <defs>
                         <marker id="markerSquare" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
                             <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="white" fillOpacity="0.8" />
@@ -2140,7 +2112,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
                             <path d="M2,2 L2,6 L6,4 L2,2 Z" stroke="#69a6db" strokeLinejoin="round" strokeWidth="1" fill="white" fillOpacity="0.8" />
                         </marker>
                     </defs>
-                    {PresBox.Instance.paths}
+                    {this.props.presPaths()}
                 </svg>
             </>
         );
@@ -2180,8 +2152,6 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
                     />
                 )}
                 {this.presPaths}
-                {this.progressivize}
-                {this.zoomProgressivize}
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 465dbfe6d..e5a2d9007 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -9,7 +9,7 @@ import { DragManager, dropActionType } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
-import { DocumentView } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionMulticolumnView.scss';
 import ResizeBar from './MulticolumnResizer';
@@ -241,9 +241,9 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     };
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () =>
-        ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
+    focusDocument = (doc: Doc, options: DocFocusOptions) => this.props.focus(this.rootDoc, options);
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+    isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false);
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return (
             <DocumentView
@@ -267,7 +267,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 hideResizeHandles={this.props.childHideResizeHandles?.()}
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 fitContentsToBox={this.props.fitContentsToBox}
-                focus={this.props.focus}
+                focus={this.focusDocument}
                 docFilters={this.childDocFilters}
                 docRangeFilters={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index f8de4e5de..d7f317f4f 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -9,7 +9,7 @@ import { DragManager, dropActionType } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
-import { DocumentView } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
@@ -241,9 +241,9 @@ export class CollectionMultirowView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     };
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () =>
-        ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
+    focusDocument = (doc: Doc, options: DocFocusOptions) => this.props.focus(this.rootDoc, options);
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+    isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false);
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return (
             <DocumentView
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 6cadeec41..e437d295e 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -198,9 +198,9 @@
         > .documentView-titleWrapper-hover {
             display: inline-block;
         }
-    }
-
-    > .documentView-styleWrapper {
+        > .documentView-contentsView {
+            opacity: 0.5;
+        }
         > .documentView-captionWrapper {
             opacity: 1;
         }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 9e3b42cf6..b495a9399 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -93,7 +93,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable _expandBoolean: boolean = false;
     @observable _transitionTools: boolean = false;
     @observable _newDocumentTools: boolean = false;
-    @observable _progressivizeTools: boolean = false;
     @observable _openMovementDropdown: boolean = false;
     @observable _openEffectDropdown: boolean = false;
     @observable _presentTools: boolean = false;
@@ -206,8 +205,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         targetDoc._viewTransition = 'all 1s';
         setTimeout(() => (targetDoc._viewTransition = undefined), 1010);
         this.nextKeyframe(targetDoc, activeItem);
-        if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
-        else targetDoc.keyFrameEditing = true;
+        targetDoc.keyFrameEditing = true;
     };
 
     _mediaTimer!: [NodeJS.Timeout, Doc];
@@ -246,22 +244,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // Called when the user activates 'next' - to move to the next part of the pres. trail
     @action
     next = () => {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        const lastFrame = Cast(targetDoc?.lastFrame, 'number', null);
-        const curFrame = NumCast(targetDoc?._currentFrame);
-        let internalFrames: boolean = false;
-        if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
-        if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) {
-            // Case 1: There are still other frames and should go through all frames before going to next slide
-            this.nextInternalFrame(targetDoc, activeItem);
-        } else if (this.childDocs[this.itemIndex + 1] !== undefined) {
-            // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide
+        if (this.childDocs[this.itemIndex + 1] !== undefined) {
+            // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide
             const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]);
             const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
             this.nextSlide(curLast + 1);
         } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
-            // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode
+            // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode
             this.nextSlide(0);
         }
         return this.itemIndex;
@@ -551,51 +540,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     }
 
-    /**
-     * Uses the viewfinder to progressivize through the different views of a single collection.
-     * @param activeItem: document for which internal zoom is used
-     */
-    zoomProgressivizeNext = (activeItem: Doc) => {
-        const targetDoc: Doc = this.targetDoc;
-        const srcContext = Cast(targetDoc?.context, Doc, null);
-        const docView = DocumentManager.Instance.getDocumentView(targetDoc);
-        const vfLeft = this.checkList(targetDoc, activeItem['viewfinder-left-indexed']);
-        const vfWidth = this.checkList(targetDoc, activeItem['viewfinder-width-indexed']);
-        const vfTop = this.checkList(targetDoc, activeItem['viewfinder-top-indexed']);
-        const vfHeight = this.checkList(targetDoc, activeItem['viewfinder-height-indexed']);
-        // Case 1: document that is not a Golden Layout tab
-        if (srcContext) {
-            const srcDocView = DocumentManager.Instance.getDocumentView(srcContext);
-            if (srcDocView) {
-                const layoutdoc = Doc.Layout(targetDoc);
-                const panelWidth: number = srcDocView.props.PanelWidth();
-                const panelHeight: number = srcDocView.props.PanelHeight();
-                const newPanX = NumCast(targetDoc.x) + NumCast(layoutdoc._width) / 2;
-                const newPanY = NumCast(targetDoc.y) + NumCast(layoutdoc._height) / 2;
-                const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
-                srcContext._panX = newPanX + (vfLeft + vfWidth / 2);
-                srcContext._panY = newPanY + (vfTop + vfHeight / 2);
-                srcContext._viewScale = newScale;
-            }
-        }
-        // Case 2: document is the containing collection
-        if (docView && !srcContext) {
-            const panelWidth: number = docView.props.PanelWidth();
-            const panelHeight: number = docView.props.PanelHeight();
-            const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
-            targetDoc._panX = vfLeft + vfWidth / 2;
-            targetDoc._panY = vfTop + vfWidth / 2;
-            targetDoc._viewScale = newScale;
-        }
-        const resize = document.getElementById('resizable');
-        if (resize) {
-            resize.style.width = vfWidth + 'px';
-            resize.style.height = vfHeight + 'px';
-            resize.style.top = vfTop + 'px';
-            resize.style.left = vfLeft + 'px';
-        }
-    };
-
     /**
      * For 'Hide Before' and 'Hide After' buttons making sure that
      * they are hidden each time the presentation is updated.
@@ -643,6 +587,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc
     @action
     startAutoPres = async (startSlide: number) => {
+        if (!this.childDocs.length) return;
         this.layoutDoc.presStatus = PresStatus.Autoplay;
         this.startPresentation(startSlide + 1 === this.childDocs.length ? 0 : startSlide);
         clearTimeout(this._presTimer);
@@ -682,14 +627,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     // The function allows for viewing the pres path on toggle
-    @action togglePath = (srcContext: Doc, off?: boolean) => {
-        if (off) {
-            this._pathBoolean = false;
-            srcContext.presPathView = false;
-        } else {
-            runInAction(() => (this._pathBoolean = !this._pathBoolean));
-            srcContext.presPathView = this._pathBoolean;
-        }
+    @action togglePath = (off?: boolean) => {
+        this._pathBoolean = off ? false : !this._pathBoolean;
+        CollectionFreeFormView.ShowPresPaths = this._pathBoolean;
     };
 
     // The function allows for expanding the view of pres on toggle
@@ -1014,17 +954,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    /**
-     *
-     */
-    @action
-    viewPaths = () => {
-        const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
-        if (srcContext) {
-            this.togglePath(srcContext);
-        }
-    };
-
     getAllIndexes = (arr: Doc[], val: Doc) => arr.map((doc, i) => (doc === val ? i : -1)).filter(i => i !== -1);
 
     // Adds the index in the pres path graphically
@@ -1104,16 +1033,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @computed get paths() {
         let pathPoints = '';
-        const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
         this.childDocs.forEach((doc, index) => {
             const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
-            const srcContext = Cast(tagDoc?.context, Doc, null);
-            if (tagDoc && presCollection === srcContext) {
+            if (tagDoc) {
                 const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
                 const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
                 if ((index = 0)) pathPoints = n1x + ',' + n1y;
                 else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
-            } else if (doc.presPinView && presCollection === tagDoc) {
+            } else if (doc.presPinView) {
                 const n1x = NumCast(doc.presPinViewX);
                 const n1y = NumCast(doc.presPinViewY);
                 if ((index = 0)) pathPoints = n1x + ',' + n1y;
@@ -1137,6 +1064,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             />
         );
     }
+    getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection.  or better yet, draw everything in an overlay layer instad of within a collection
 
     // Converts seconds to ms and updates presTransition
     setTransitionTime = (number: String, change?: number) => {
@@ -1840,7 +1768,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // CollectionFreeFormDocumentView.setupScroll(tagDoc, 0);
             // CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
         }
-        // if (tagDoc.editScrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(tagDoc, currentFrame);
         CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
         tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
         tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
@@ -1882,123 +1809,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openActiveColorPicker: boolean = false;
     @observable private openViewedColorPicker: boolean = false;
 
-    @computed get progressivizeDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        if (activeItem && targetDoc) {
-            const activeFontColor = targetDoc['pres-text-color'] ? StrCast(targetDoc['pres-text-color']) : 'Black';
-            const viewedFontColor = targetDoc['pres-text-viewed-color'] ? StrCast(targetDoc['pres-text-viewed-color']) : 'Black';
-            return (
-                <div className={`presBox-ribbon ${this._progressivizeTools && this.layoutDoc.presStatus === 'edit' ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
-                    <div className="ribbon-box">
-                        {this.stringType} selected
-                        <div
-                            className="ribbon-doubleButton"
-                            style={{
-                                borderTop: 'solid 1px darkgrey',
-                                display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
-                            }}>
-                            <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeChild}>
-                                Contents
-                            </div>
-                            <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editProgressivize}>
-                                Edit
-                            </div>
-                        </div>
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
-                            <div className="presBox-subheading">Active text color</div>
-                            <div
-                                className="ribbon-colorBox"
-                                style={{ backgroundColor: activeFontColor, height: 15, width: 15 }}
-                                onClick={action(() => {
-                                    this.openActiveColorPicker = !this.openActiveColorPicker;
-                                })}></div>
-                        </div>
-                        {this.activeColorPicker}
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
-                            <div className="presBox-subheading">Viewed font color</div>
-                            <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => (this.openViewedColorPicker = !this.openViewedColorPicker))}></div>
-                        </div>
-                        {this.viewedColorPicker}
-                        <div
-                            className="ribbon-doubleButton"
-                            style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? 'inline-flex' : 'none' }}>
-                            <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeZoom}>
-                                Zoom
-                            </div>
-                            <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editZoomProgressivize}>
-                                Edit
-                            </div>
-                        </div>
-                        <div
-                            className="ribbon-doubleButton"
-                            style={{
-                                borderTop: 'solid 1px darkgrey',
-                                display: targetDoc._viewType === 'stacking' || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
-                            }}>
-                            <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeScroll}>
-                                Scroll
-                            </div>
-                            <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editScrollProgressivize}>
-                                Edit
-                            </div>
-                        </div>
-                    </div>
-                    <div className="ribbon-final-box">
-                        Frames
-                        <div className="ribbon-doubleButton">
-                            <div className="ribbon-frameSelector">
-                                <div
-                                    key="back"
-                                    title="back frame"
-                                    className="backKeyframe"
-                                    onClick={e => {
-                                        e.stopPropagation();
-                                        this.prevKeyframe(targetDoc, activeItem);
-                                    }}>
-                                    <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
-                                </div>
-                                <div
-                                    key="num"
-                                    title="toggle view all"
-                                    className="numKeyframe"
-                                    style={{ color: targetDoc.keyFrameEditing ? 'white' : 'black', backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
-                                    onClick={action(() => (targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}>
-                                    {NumCast(targetDoc._currentFrame)}
-                                </div>
-                                <div
-                                    key="fwd"
-                                    title="forward frame"
-                                    className="fwdKeyframe"
-                                    onClick={e => {
-                                        e.stopPropagation();
-                                        this.nextKeyframe(targetDoc, activeItem);
-                                    }}>
-                                    <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
-                                </div>
-                            </div>
-                            <Tooltip
-                                title={
-                                    <>
-                                        <div className="dash-tooltip">{'Last frame'}</div>
-                                    </>
-                                }>
-                                <div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div>
-                            </Tooltip>
-                        </div>
-                        <div className="ribbon-frameList">
-                            {this.frameListHeader}
-                            {this.frameList}
-                        </div>
-                        <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(' TODO: play frames')}>
-                            Play
-                        </div>
-                    </div>
-                </div>
-            );
-        }
-    }
-
     @undoBatch
     @action
     switchActive = (color: ColorState) => {
@@ -2033,262 +1843,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @action
-    turnOffEdit = (paths?: boolean) => {
-        // Turn off paths
-        if (paths) {
-            const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
-            if (srcContext) this.togglePath(srcContext, true);
-        }
-        // Turn off the progressivize editors for each document
-        this.childDocs.forEach(doc => {
-            doc.editSnapZoomProgressivize = false;
-            doc.editZoomProgressivize = false;
-            const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
-            if (targetDoc) {
-                targetDoc.editZoomProgressivize = false;
-                // targetDoc.editScrollProgressivize = false;
-            }
-        });
-    };
-
-    //Toggle whether the user edits or not
-    @action
-    editZoomProgressivize = (e: React.MouseEvent) => {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        if (!targetDoc.editZoomProgressivize) {
-            if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true;
-            targetDoc.zoomProgressivize = true;
-            targetDoc.editZoomProgressivize = true;
-            activeItem.editZoomProgressivize = true;
-        } else {
-            targetDoc.editZoomProgressivize = false;
-            activeItem.editZoomProgressivize = false;
-        }
-    };
-
-    //Toggle whether the user edits or not
-    @action
-    editScrollProgressivize = (e: React.MouseEvent) => {
-        const targetDoc: Doc = this.targetDoc;
-        if (!targetDoc.editScrollProgressivize) {
-            if (!targetDoc.scrollProgressivize) {
-                targetDoc.scrollProgressivize = true;
-                this.activeItem.scrollProgressivize = true;
-            }
-            targetDoc.editScrollProgressivize = true;
-        } else {
-            targetDoc.editScrollProgressivize = false;
-        }
-    };
-
-    //Progressivize Zoom
-    @action
-    progressivizeScroll = (e: React.MouseEvent) => {
-        e.stopPropagation();
-        this.activeItem.scrollProgressivize = !this.activeItem.scrollProgressivize;
-        const targetDoc: Doc = this.targetDoc;
-        targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize;
-        // CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame));
-        if (targetDoc.editScrollProgressivize) {
-            targetDoc.editScrollProgressivize = false;
-            targetDoc._currentFrame = 0;
-            targetDoc.lastFrame = 0;
-        }
-    };
-
-    //Progressivize Zoom
-    @action
-    progressivizeZoom = (e: React.MouseEvent) => {
-        e.stopPropagation();
-        const activeItem: Doc = this.activeItem;
-        activeItem.zoomProgressivize = !activeItem.zoomProgressivize;
-        const targetDoc: Doc = this.targetDoc;
-        targetDoc.zoomProgressivize = !targetDoc.zoomProgressivize;
-        CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc);
-        if (activeItem.editZoomProgressivize) {
-            activeItem.editZoomProgressivize = false;
-            targetDoc._currentFrame = 0;
-            targetDoc.lastFrame = 0;
-        }
-    };
-
-    //Progressivize Child Docs
-    @action
-    editProgressivize = (e: React.MouseEvent) => {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        targetDoc._currentFrame = targetDoc.lastFrame;
-        if (!targetDoc.editProgressivize) {
-            if (!activeItem.presProgressivize) {
-                activeItem.presProgressivize = true;
-                targetDoc.presProgressivize = true;
-            }
-            targetDoc.editProgressivize = true;
-        } else {
-            targetDoc.editProgressivize = false;
-        }
-    };
-
-    @action
-    progressivizeChild = (e: React.MouseEvent) => {
-        e.stopPropagation();
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
-        if (!activeItem.presProgressivize) {
-            targetDoc.keyFrameEditing = false;
-            activeItem.presProgressivize = true;
-            targetDoc.presProgressivize = true;
-            targetDoc._currentFrame = 0;
-            docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true));
-            targetDoc.lastFrame = targetDoc.lastFrame ? NumCast(targetDoc.lastFrame) : docs.length - 1;
-        } else {
-            // targetDoc.editProgressivize = false;
-            activeItem.presProgressivize = false;
-            targetDoc.presProgressivize = false;
-            targetDoc._currentFrame = 0;
-            targetDoc.keyFrameEditing = true;
-        }
-    };
-
-    @action
-    checkMovementLists = (doc: Doc, xlist: any, ylist: any) => {
-        const x: List<number> = xlist;
-        const y: List<number> = ylist;
-        const tags: JSX.Element[] = [];
-        let pathPoints = ''; //List of all of the pathpoints that need to be added
-        for (let i = 0; i < x.length - 1; i++) {
-            if (y[i] || x[i]) {
-                if (i === 0) pathPoints = x[i] - 11 + ',' + (y[i] + 33);
-                else pathPoints = pathPoints + ' ' + (x[i] - 11) + ',' + (y[i] + 33);
-                tags.push(
-                    <div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}>
-                        {i}
-                    </div>
-                );
-            }
-        }
-        tags.push(
-            <svg style={{ overflow: 'visible', position: 'absolute' }}>
-                <polyline
-                    points={pathPoints}
-                    style={{
-                        position: 'absolute',
-                        opacity: 1,
-                        stroke: '#000000',
-                        strokeWidth: 2,
-                        strokeDasharray: '10 5',
-                    }}
-                    fill="none"
-                />
-            </svg>
-        );
-        return tags;
-    };
-
-    @observable
-    toggleDisplayMovement = (doc: Doc) => (doc.displayMovement = !doc.displayMovement);
-
-    @action
-    checkList = (doc: Doc, list: any): number => {
-        const x: List<number> = list;
-        if (x?.length >= NumCast(doc._currentFrame) + 1) {
-            return x[NumCast(doc._currentFrame)];
-        } else if (x) {
-            x.length = NumCast(doc._currentFrame) + 1;
-            x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1];
-            return x[NumCast(doc._currentFrame)];
-        }
-        return 100;
-    };
-
-    @computed get progressivizeChildDocs() {
-        const targetDoc: Doc = this.targetDoc;
-        const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
-        const tags: JSX.Element[] = [];
-        docs.forEach((doc, index) => {
-            if (doc['x-indexed'] && doc['y-indexed']) {
-                tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? 'block' : 'none' }}>{this.checkMovementLists(doc, doc['x-indexed'], doc['y-indexed'])}</div>);
-            }
-            tags.push(
-                <div
-                    className="progressivizeButton"
-                    key={index}
-                    onPointerLeave={() => {
-                        if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0;
-                    }}
-                    onPointerOver={() => {
-                        if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5;
-                    }}
-                    onClick={e => {
-                        this.toggleDisplayMovement(doc);
-                        e.stopPropagation();
-                    }}
-                    style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : '#c8c8c8', top: NumCast(doc.y), left: NumCast(doc.x) }}>
-                    <div className="progressivizeButton-prev">
-                        <FontAwesomeIcon
-                            icon={'caret-left'}
-                            size={'lg'}
-                            onClick={e => {
-                                e.stopPropagation();
-                                this.prevAppearFrame(doc, index);
-                            }}
-                        />
-                    </div>
-                    <div className="progressivizeButton-frame">{NumCast(doc.appearFrame)}</div>
-                    <div className="progressivizeButton-next">
-                        <FontAwesomeIcon
-                            icon={'caret-right'}
-                            size={'lg'}
-                            onClick={e => {
-                                e.stopPropagation();
-                                this.nextAppearFrame(doc, index);
-                            }}
-                        />
-                    </div>
-                </div>
-            );
-        });
-        return tags;
-    }
-
-    @action
-    nextAppearFrame = (doc: Doc, i: number) => {
-        doc.appearFrame = (Cast(doc.appearFrame, 'number', null) ?? 0) + 1;
-        this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
-    };
-
-    @action
-    prevAppearFrame = (doc: Doc, i: number) => {
-        doc.appearFrame = Math.max(0, (Cast(doc.appearFrame, 'number', null) ?? 0) - 1);
-        this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
-    };
-
-    @action
-    updateOpacityList = (list: any, frame: number) => {
-        const x: List<number> = list;
-        if (x && x.length >= frame) {
-            for (let i = 0; i < x.length; i++) {
-                if (i < frame) {
-                    x[i] = 0;
-                } else if (i >= frame) {
-                    x[i] = 1;
-                }
-            }
-            list = x;
-        } else {
-            x.length = frame + 1;
-            for (let i = 0; i < x.length; i++) {
-                if (i < frame) {
-                    x[i] = 0;
-                } else if (i >= frame) {
-                    x[i] = 1;
-                }
-            }
-            list = x;
-        }
-    };
+    turnOffEdit = (paths?: boolean) => paths && this.togglePath(true); // Turn off paths
 
     @computed
     get toolbarWidth(): number {
@@ -2311,11 +1866,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <FontAwesomeIcon icon={"plus"} />
                     <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} />
                 </div></Tooltip> */}
-                <Tooltip title={<div className="dash-tooltip">{'View paths'}</div>}>
+                <Tooltip title={<div className="dash-tooltip">View paths</div>}>
                     <div
-                        style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
+                        style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
                         className={'toolbar-button'}
-                        onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
+                        onClick={this.childDocs.length > 1 ? () => this.togglePath() : undefined}>
                         <FontAwesomeIcon icon={'exchange-alt'} />
                     </div>
                 </Tooltip>
-- 
cgit v1.2.3-70-g09d2


From 19f317bd43a7cc8df0daf1c0642011cc8754e14b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 15 Nov 2022 12:49:53 -0500
Subject: made InPlace container tool button apply more settings to be more
 like a template. added followAllLInks flag.  added image anchors to save pan
 zoom.  added follow link button bar option for follow all links.  added
 hideDecorations flag and property

---
 src/client/documents/Documents.ts                  |  5 ++
 src/client/util/LinkFollower.ts                    | 11 ++--
 src/client/views/DocumentButtonBar.scss            | 31 ++++++++++-
 src/client/views/DocumentButtonBar.tsx             | 60 +++++++++++++++-------
 src/client/views/DocumentDecorations.tsx           | 19 +++----
 src/client/views/PropertiesButtons.tsx             | 17 ++++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/linking/LinkEditor.tsx            |  1 -
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/ImageBox.tsx                | 52 ++++++++++++++++---
 src/client/views/nodes/trails/PresBox.tsx          |  3 +-
 11 files changed, 154 insertions(+), 50 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e44004f45..8f45802fe 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -257,6 +257,7 @@ export class DocumentOptions {
     lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide)
     activeFrame?: number; // the active frame of a document in a frame base collection
     appearFrame?: number; // the frame in which the document appears
+    viewTransitionTime?: number; // transition duration for view parameters
     presTransition?: number; //the time taken for the transition TO a document
     presDuration?: number; //the duration of the slide in presentation view
     borderRounding?: string;
@@ -1047,6 +1048,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
         }
 
+        export function ImageanchorDocument(options: DocumentOptions = {}, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+        }
+
         export function HTMLAnchorDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), options, id);
         }
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index c09c9d1c5..c6fc7b372 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,6 +1,7 @@
 import { action, runInAction } from 'mobx';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, DocCast, StrCast } from '../../fields/Types';
+import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentDecorations } from '../views/DocumentDecorations';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentViewSharedProps, ViewAdjustment } from '../views/nodes/DocumentView';
@@ -76,11 +77,11 @@ export class LinkFollower {
         const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
         const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
         const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
-        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
-        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor2 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor2 as Doc).annotationOn) : (d.anchor2 as Doc)).length === 0);
+        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor1 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor1 as Doc).annotationOn) : (d.anchor1 as Doc)).length === 0);
         const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
-        const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs;
-        const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1);
+        const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs;
+        const followLinks = sourceDoc.isPushpin || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1);
         var count = 0;
         const allFinished = () => ++count === followLinks.length && finished?.();
         followLinks.forEach(async linkDoc => {
diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss
index 1e93ba5e2..f9c988fdd 100644
--- a/src/client/views/DocumentButtonBar.scss
+++ b/src/client/views/DocumentButtonBar.scss
@@ -18,15 +18,44 @@ $linkGap: 3px;
     cursor: pointer;
 }
 
+.documentButtonBar-followTypes,
 .documentButtonBar-pinTypes {
     position: absolute;
-    display: flex;
+    display: none;
     width: 60px;
     top: -14px;
     background: black;
     height: 20px;
     align-items: center;
 }
+.documentButtonBar-followTypes {
+    width: 20px;
+    display: none;
+}
+.documentButtonBar-followIcon {
+    align-items: center;
+    display: flex;
+    height: 100%;
+    &:hover {
+        background-color: lightblue;
+    }
+}
+.documentButtonBar-follow {
+    &:hover {
+        .documentButtonBar-followTypes {
+            display: flex;
+        }
+    }
+}
+.documentButtonBar-pin {
+    color: white;
+    &:hover {
+        .documentButtonBar-pinTypes {
+            display: flex;
+        }
+    }
+}
+
 .documentButtonBar-pinIcon {
     &:hover {
         background-color: lightblue;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 36875290e..794b51cc5 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -5,7 +5,7 @@ import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast } from '../../fields/Types';
+import { BoolCast, Cast, NumCast } from '../../fields/Types';
 import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
@@ -209,21 +209,52 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             </Tooltip>
         );
     }
+    @observable subFollow = '';
     @computed
     get followLinkButton() {
         const targetDoc = this.view0?.props.Document;
+        const followBtn = (allDocs: boolean, click: (doc: Doc) => void, isSet: (doc?: Doc) => boolean, icon: IconProp) => {
+            const tooltip = `Follow ${this.subPin}documents`;
+            return !tooltip ? null : (
+                <Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
+                    <div className="documentButtonBar-followIcon" style={{ backgroundColor: isSet(targetDoc) ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: isSet(targetDoc) ? Colors.BLACK : Colors.WHITE }}>
+                        <FontAwesomeIcon
+                            className="documentdecorations-icon"
+                            style={{ width: 20 }}
+                            key={icon.toString()}
+                            size="sm"
+                            icon={icon}
+                            onPointerEnter={action(e => (this.subPin = allDocs ? 'All ' : ''))}
+                            onPointerLeave={action(e => (this.subPin = ''))}
+                            onClick={e => {
+                                this.props.views().forEach(dv => click(dv!.rootDoc));
+                                e.stopPropagation();
+                            }}
+                        />
+                    </div>
+                </Tooltip>
+            );
+        };
         return !targetDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">{'Set onClick to follow primary link'}</div>}>
+            <Tooltip title={<div className="dash-tooltip">Set onClick to follow primary link</div>}>
                 <div
-                    className="documentButtonBar-icon"
+                    className="documentButtonBar-icon documentButtonBar-follow"
                     style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
                     onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
+                    <div className="documentButtonBar-followTypes">
+                        {followBtn(
+                            true,
+                            (doc: Doc) => (doc.followAllLinks = !doc.followAllLinks),
+                            (doc?: Doc) => (doc?.followAllLinks ? true : false),
+                            'window-maximize'
+                        )}
+                    </div>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="hand-point-right" />
                 </div>
             </Tooltip>
         );
     }
-    @observable expandPin = false;
+
     @observable subPin = '';
     @computed
     get pinButton() {
@@ -264,10 +295,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         return !targetDoc ? null : (
             <Tooltip title={<div className="dash-tooltip">{`Pin Document ${SelectionManager.Views().length > 1 ? 'multiple documents' : ''} to Trail`}</div>}>
                 <div
-                    className="documentButtonBar-icon"
-                    style={{ color: 'white' }}
-                    onPointerEnter={action(e => (this.expandPin = true))}
-                    onPointerLeave={action(e => (this.expandPin = false))}
+                    className="documentButtonBar-icon documentButtonBar-pin"
                     onClick={e => {
                         const docs = this.props
                             .views()
@@ -276,13 +304,11 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         TabDocView.PinDoc(docs, { pinDocLayout: e.shiftKey, pinDocContent: e.altKey, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
                         e.stopPropagation();
                     }}>
-                    {this.expandPin ? (
-                        <div className="documentButtonBar-pinTypes">
-                            {pinBtn(true, false, 'window-maximize')}
-                            {pinBtn(false, true, 'address-card')}
-                            {pinBtn(true, true, 'id-card')}
-                        </div>
-                    ) : null}
+                    <div className="documentButtonBar-pinTypes">
+                        {pinBtn(true, false, 'window-maximize')}
+                        {pinBtn(false, true, 'address-card')}
+                        {pinBtn(true, true, 'id-card')}
+                    </div>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
                 </div>
             </Tooltip>
@@ -460,9 +486,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                 ) : null}
                 {
                     Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.templateButton}</div>
-                    /*<div className="documentButtonBar-button">
-                        {this.metadataButton}
-                    </div> */
+                    /*<div className="documentButtonBar-button"> {this.metadataButton} </div> */
                 }
                 {Doc.noviceMode || !SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
                 <div className="documentButtonBar-button">{this.pinButton}</div>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3fac137fe..3efb5fb37 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -708,24 +708,25 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     }
 
     render() {
-        const { b, c, r, x, y } = this.Bounds;
-        const bounds = { b, c, r, x, y };
+        const { b, r, x, y } = this.Bounds;
+        const bounds = { b, r, x, y };
         const seldocview = SelectionManager.Views().slice(-1)[0];
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return null;
         }
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
-        const hideResizers = seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating;
-        const hideTitle = seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
-        const hideDocumentButtonBar = seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
+        const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
+        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating;
+        const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
+        const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
-        const hideOpenButton =
+        const hideOpenButton =hideDecorations || 
             seldocview.props.hideOpenButton ||
             seldocview.rootDoc.hideOpenButton ||
             SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) ||
             this._isRounding ||
             this._isRotating;
-        const hideDeleteButton =
+        const hideDeleteButton =hideDecorations || 
             this._isRounding ||
             this._isRotating ||
             seldocview.props.hideDeleteButton ||
@@ -767,7 +768,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
 
         const useLock = bounds.r - bounds.x > 135 && seldocview.props.CollectionFreeFormDocumentView;
-        const useRotation = seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.props.CollectionFreeFormDocumentView; // when do we want an object to not rotate?
+        const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.props.CollectionFreeFormDocumentView; // when do we want an object to not rotate?
         const rotation = NumCast(seldocview.rootDoc._rotation);
 
         const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
@@ -837,7 +838,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             <div className="documentDecorations-topbar" onPointerDown={this.onContainerDown}>
                                 {hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
-                                {titleArea}
+                                {hideTitle ? null : titleArea}
                                 {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 5992b3eb9..d27848a90 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -121,15 +121,24 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             onClick => {
                 SelectionManager.Views().forEach(dv => {
                     const containerDoc = dv.rootDoc;
-                    containerDoc.noShadow = containerDoc.noHighlighting = containerDoc._isLinkButton = containerDoc._fitContentsToBox = containerDoc._forceActive = containerDoc._isInPlaceContainer = !containerDoc._isInPlaceContainer;
+                    containerDoc.followAllLinks =
+                        containerDoc.noShadow =
+                        containerDoc.noHighlighting =
+                        containerDoc._isLinkButton =
+                        containerDoc._fitContentsToBox =
+                        containerDoc._forceActive =
+                        containerDoc._isInPlaceContainer =
+                            !containerDoc._isInPlaceContainer;
                     containerDoc.followLinkLocation = containerDoc._isInPlaceContainer ? 'inPlace' : undefined;
                     containerDoc._xPadding = containerDoc._yPadding = containerDoc._isInPlaceContainer ? 10 : undefined;
                     const menuDoc = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]).lastElement();
                     if (menuDoc) {
-                        menuDoc._forceActive = menuDoc._fitContentsToBox = menuDoc._isLinkButton = menuDoc._noShadow = menuDoc.noHighlighting = containerDoc._isInPlaceContainer;
-                        DocUtils.MakeLink({ doc: dv.rootDoc }, { doc: menuDoc }, 'back link to container');
+                        menuDoc.hideDecorations = menuDoc._forceActive = menuDoc._fitContentsToBox = menuDoc._isLinkButton = menuDoc._noShadow = menuDoc.noHighlighting = containerDoc._isInPlaceContainer;
+                        if (!dv.allLinks.find(link => link.anchor1 === menuDoc || link.anchor2 === menuDoc)) {
+                            DocUtils.MakeLink({ doc: dv.rootDoc }, { doc: menuDoc }, 'back link to container');
+                        }
                         DocListCast(menuDoc[Doc.LayoutFieldKey(menuDoc)]).forEach(menuItem => {
-                            menuItem._isLinkButton = true;
+                            menuItem.followAllLinks = menuItem._isLinkButton = true;
                             menuItem._followLinkLocation = 'inPlace';
                         });
                     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e24b116d0..8fe5ad63f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1157,7 +1157,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         // if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
         //     SelectionManager.DeselectAll();
         // }
-        if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined) {
+        if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined || doc.type === DocumentType.MARKER) {
             this.props.focus(doc, options);
         } else {
             const xfToCollection = options?.docTransform ?? Transform.Identity();
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 2fb8e3d4c..3e8867c50 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -206,7 +206,6 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     @computed
     get editAudioFollow() {
         //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS
-        console.log('AudioFollow:' + this.audioFollow);
         return (
             <div className="linkEditor-zoomFollow">
                 <div className="linkEditor-zoomFollow-label">Play Target Audio:</div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7accd49a4..76d6d3532 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -166,7 +166,8 @@ export interface DocumentViewSharedProps {
 // these props are specific to DocuentViews
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
-    hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
+    hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected
+    hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected
     hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDocumentButtonBar?: boolean;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d0df41023..d67481b22 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -14,7 +14,7 @@ import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
-import { DocUtils } from '../../documents/Documents';
+import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
 import { DragManager } from '../../util/DragManager';
@@ -49,21 +49,58 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return FieldView.LayoutString(ImageBox, fieldKey);
     }
     private _dropDisposer?: DragManager.DragDropDisposer;
+    private _transitioning: any; // timer for setting view spec properties
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     @observable _curSuffix = '';
     @observable _uploadIcon = uploadIcons.idle;
+    @observable _focusViewScale: number | undefined = 1;
+    @observable _focusPanX: number | undefined = 0;
+    @observable _focusPanY: number | undefined = 0;
+    get viewScale() {
+        return this._focusViewScale || StrCast(this.layoutDoc._viewScale);
+    }
+    get panX() {
+        return this._focusPanX || StrCast(this.layoutDoc._panX);
+    }
+    get panY() {
+        return this._focusPanY || StrCast(this.layoutDoc._panY);
+    }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
     };
-    setViewSpec = (anchor: Doc, preview: boolean) => {}; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
+
+    @action
+    setViewSpec = (anchor: Doc, preview: boolean) => {
+        if (preview && anchor._viewScale !== undefined) {
+            this._focusViewScale = Cast(anchor._viewScale, 'number', null);
+            this._focusPanX = Cast(anchor._panX, 'number', null);
+            this._focusPanY = Cast(anchor._panX, 'number', null);
+        } else if (anchor._viewScale !== undefined) {
+            const smoothTime = NumCast(anchor.viewTransitionTime);
+            this.layoutDoc.viewTransition = `all ${smoothTime}ms`;
+            this.layoutDoc._panX = NumCast(anchor._panX, NumCast(this.layoutDoc._panY));
+            this.layoutDoc._panY = NumCast(anchor._panY, NumCast(this.layoutDoc._panX));
+            this.layoutDoc._viewScale = NumCast(anchor._viewScale, NumCast(this.layoutDoc._viewScale));
+            if (anchor.type === DocumentType.MARKER) {
+                this.dataDoc[this.annotationKey] = new List<Doc>(DocListCast(anchor._annotations));
+            }
+            clearTimeout(this._transitioning);
+            this._transitioning = setTimeout(() => (this.layoutDoc.viewTransition = undefined), smoothTime);
+        }
+    }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
     getAnchor = () => {
-        const anchor = this._getAnchor?.(this._savedAnnotations);
-        anchor && this.addDocument(anchor);
-        return anchor ?? this.rootDoc;
+        const zoomedAnchor = () => Docs.Create.ImageanchorDocument({ viewTransitionTime: 1000, _viewScale: NumCast(this.layoutDoc._viewScale), _panX: NumCast(this.layoutDoc._panX), _panY: NumCast(this.layoutDoc._panY) });
+        const anchor = this._getAnchor?.(this._savedAnnotations) ?? (this.layoutDoc.viewScale ? zoomedAnchor() : undefined);
+        if (anchor) {
+            anchor._annotations = new List<Doc>(DocListCast(this.dataDoc[this.annotationKey]));
+            this.addDocument(anchor);
+            return anchor;
+        }
+        return this.rootDoc;
     };
 
     componentDidMount() {
@@ -90,6 +127,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     componentWillUnmount() {
+        clearTimeout(this._transitioning);
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
@@ -283,9 +321,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     setTimeout(
                         action(() => {
                             this._uploadIcon = idle;
-                            if (data) {
-                                dataDoc[this.fieldKey] = data;
-                            }
+                            data && (dataDoc[this.fieldKey] = data);
                         }),
                         2000
                     );
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index b495a9399..8d805c663 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -509,8 +509,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static NavigateToTarget(targetDoc: Doc, activeItem: Doc, openInTab: any, srcContext: Doc, finished?: () => void) {
         if ((activeItem.presPinLayout || activeItem.presPinView) && DocCast(targetDoc.context)?._currentFrame === undefined) {
             const transTime = NumCast(activeItem.presTransition, 500);
-            const presTransitionTime = `all ${transTime}ms`;
-            targetDoc._dataTransition = presTransitionTime;
+            targetDoc._dataTransition = `all ${transTime}ms`;
             targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x));
             targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y));
             targetDoc.rotation = NumCast(activeItem.presRot, NumCast(targetDoc.rotation));
-- 
cgit v1.2.3-70-g09d2


From ae324ff50865929be836edf3bbf129207638a9c9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Nov 2022 16:26:31 -0500
Subject: big changes to make link following use the same code as pinning docs
 for trails.

---
 src/client/documents/Documents.ts                  |   1 +
 src/client/util/DocumentManager.ts                 |  30 ++---
 src/client/util/LinkFollower.ts                    |   7 +-
 src/client/views/DocumentButtonBar.tsx             |   6 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 src/client/views/MarqueeAnnotator.tsx              |   1 +
 src/client/views/StyleProvider.tsx                 |   2 +-
 .../collections/CollectionStackedTimeline.tsx      |   2 +-
 src/client/views/collections/TabDocView.tsx        |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  18 ++-
 src/client/views/nodes/ComparisonBox.tsx           |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   8 +-
 src/client/views/nodes/ImageBox.tsx                |  60 ++++------
 src/client/views/nodes/PDFBox.tsx                  |  16 ++-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          | 131 +++++++++++++--------
 src/client/views/pdf/Annotation.tsx                |   2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  11 +-
 18 files changed, 178 insertions(+), 129 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8f45802fe..e68b9e27b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -237,6 +237,7 @@ export class DocumentOptions {
     childContextMenuScripts?: List<ScriptField>;
     childContextMenuLabels?: List<string>;
     childContextMenuIcons?: List<string>;
+    followLinkZoom?: boolean; // whether to zoom to the target of a link
     hideLinkButton?: boolean; // whether the blue link counter button should be hidden
     hideDecorationTitle?: boolean;
     hideOpenButton?: boolean;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 9336717c0..a60c1ed6b 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -151,6 +151,21 @@ export class DocumentManager {
         return toReturn;
     }
 
+    static playAudioAnno(doc: Doc) {
+        const anno = Cast(doc[Doc.LayoutFieldKey(doc) + '-audioAnnotations'], listSpec(AudioField), null)?.lastElement();
+        if (anno) {
+            if (anno instanceof AudioField) {
+                new Howl({
+                    src: [anno.url.href],
+                    format: ['mp3'],
+                    autoplay: true,
+                    loop: false,
+                    volume: 0.5,
+                });
+            }
+        }
+    }
+
     static addView = (doc: Doc, finished?: () => void) => {
         CollectionDockingView.AddSplit(doc, 'right');
         finished?.();
@@ -189,20 +204,6 @@ export class DocumentManager {
             } else {
                 finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined);
                 !noSelect && docView?.select(false);
-                if (originatingDoc?.followLinkAudio) {
-                    const anno = Cast(finalTargetDoc[Doc.LayoutFieldKey(finalTargetDoc) + '-audioAnnotations'], listSpec(AudioField), null)?.lastElement();
-                    if (anno) {
-                        if (anno instanceof AudioField) {
-                            new Howl({
-                                src: [anno.url.href],
-                                format: ['mp3'],
-                                autoplay: true,
-                                loop: false,
-                                volume: 0.5,
-                            });
-                        }
-                    }
-                }
             }
             finished?.();
         };
@@ -233,6 +234,7 @@ export class DocumentManager {
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
+            if (originatingDoc?.followLinkAudio) DocumentManager.playAudioAnno(focusView.rootDoc);
             const doFocus = (forceDidFocus: boolean) =>
                 focusView.focus(originalTarget ?? targetDoc, {
                     originalTarget,
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index c6fc7b372..68716a207 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -6,6 +6,7 @@ import { DocumentDecorations } from '../views/DocumentDecorations';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentViewSharedProps, ViewAdjustment } from '../views/nodes/DocumentView';
 import { DocumentManager } from './DocumentManager';
+import { LinkManager } from './LinkManager';
 import { UndoManager } from './UndoManager';
 
 type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
@@ -25,7 +26,7 @@ export class LinkFollower {
     // follows a link - if the target is on screen, it highlights/pans to it.
     // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
     // depending on the followLinkLocation property of the source (or the link itself as a fallback);
-    public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean, zoom: boolean = false) => {
+    public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => {
         const batch = UndoManager.StartBatch('follow link click');
         // open up target if it's not already in view ...
         const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
@@ -63,7 +64,6 @@ export class LinkFollower {
             linkDoc,
             sourceDoc,
             createViewFunc,
-            BoolCast(sourceDoc.followLinkZoom, zoom),
             docViewProps.ContainingCollectionDoc,
             action(() => {
                 batch.end();
@@ -73,7 +73,7 @@ export class LinkFollower {
         );
     };
 
-    public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
+    public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
         const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
         const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
         const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
@@ -96,6 +96,7 @@ export class LinkFollower {
                         : linkDoc.anchor1
                 ) as Doc;
                 if (target) {
+                    const zoom = BoolCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkZoom, false);
                     if (target.TourMap) {
                         const fieldKey = Doc.LayoutFieldKey(target);
                         const tour = DocListCast(target[fieldKey]).reverse();
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 794b51cc5..681349ccf 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -284,7 +284,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                                     .views()
                                     .filter(v => v)
                                     .map(dv => dv!.rootDoc);
-                                TabDocView.PinDoc(docs, { pinDocLayout, pinDocContent, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+                                TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout, pinDocContent, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
                                 e.stopPropagation();
                             }}
                         />
@@ -301,7 +301,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             .views()
                             .filter(v => v)
                             .map(dv => dv!.rootDoc);
-                        TabDocView.PinDoc(docs, { pinDocLayout: e.shiftKey, pinDocContent: e.altKey, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+                        TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinDocContent: e.altKey, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
                         e.stopPropagation();
                     }}>
                     <div className="documentButtonBar-pinTypes">
@@ -488,7 +488,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                     Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.templateButton}</div>
                     /*<div className="documentButtonBar-button"> {this.metadataButton} </div> */
                 }
-                {Doc.noviceMode || !SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
+                {!SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
                 <div className="documentButtonBar-button">{this.pinButton}</div>
                 <div className="documentButtonBar-button">{this.recordButton}</div>
                 {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 5a6caf995..4890d9624 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -6,7 +6,7 @@ import { Id } from '../../fields/FieldSymbols';
 import { InkTool } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ScriptField } from '../../fields/ScriptField';
-import { Cast, PromiseValue } from '../../fields/Types';
+import { Cast, DocCast, PromiseValue } from '../../fields/Types';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -245,7 +245,7 @@ export class KeyManager {
                 if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) {
                     SelectionManager.Views()[0].ComponentView?.search?.('', false, false);
                 } else {
-                    const searchBtn = Doc.MySearcher;
+                    const searchBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MySearcher);
                     if (searchBtn) {
                         MainView.Instance.selectMenu(searchBtn);
                     }
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index c0dd62a05..1162cde50 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -93,6 +93,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                 dragComplete: e => {
                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
                         e.annoDragData.linkSourceDoc.isPushpin = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
+                        e.annoDragData.linkSourceDoc.followLinkZoom = false;
                     }
                 },
             });
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index a04f4a4f4..8ee5ebdb6 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -236,7 +236,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                                 ? Colors.DARK_GRAY
                                 : Colors.LIGHT_GRAY // system docs (seen in treeView) get a grayish background
                             : doc.annotationOn
-                            ? '#00000015' // faint interior for collections on PDFs, images, etc
+                            ? '#00000010' // faint interior for collections on PDFs, images, etc
                             : doc?._isGroup
                             ? undefined
                             : Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (darkScheme() ? Colors.BLACK : 'linear-gradient(#065fff, #85c1f9)'));
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 7bf798656..39ae470b6 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -748,7 +748,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     time < NumCast(this.props.mark[this.props.endTag]) &&
                     this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5
                 ) {
-                    LinkFollower.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false, true);
+                    LinkFollower.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false);
                 }
                 this._lastTimecode = time;
             }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 1a9006356..e21649648 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -229,7 +229,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
      * Adds a document to the presentation view
      **/
     @action
-    public static PinDoc(docs: Doc | Doc[], pinProps?: PinProps) {
+    public static PinDoc(docs: Doc | Doc[], pinProps: PinProps) {
         const docList = docs instanceof Doc ? [docs] : docs;
 
         const batch = UndoManager.StartBatch('pinning doc');
@@ -271,7 +271,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presStartTime = NumCast(doc.clipStart);
                 pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
             }
-            PresBox.pinDocView(pinDoc, pinProps, doc);
+            PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, doc);
             pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
             Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
             //save position
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 932bd789d..8a97797c7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1530,11 +1530,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return 0;
     };
 
+    @action
+    scrollFocus = (anchor: Doc, smooth: boolean) => {
+        let focusSpeed: Opt<number>;
+        PresBox.restoreTargetDocView(
+            this.rootDoc, //
+            { pinDocLayout: BoolCast(anchor.presPinDocLayout) },
+            anchor,
+            (focusSpeed = !smooth ? 0 : NumCast(anchor.presTransition)),
+            {
+                pannable: anchor.presPinData ? true : false,
+            }
+        );
+        return focusSpeed;
+    }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
+
     getAnchor = () => {
         if (this.props.Document.annotationOn) {
             return this.rootDoc;
         }
-        const anchor = Docs.Create.TextanchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
+        const anchor = Docs.Create.TextanchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), presTransition: 500, annotationOn: this.rootDoc });
+        PresBox.pinDocView(anchor, { pinData: { pannable: true } }, this.rootDoc);
         const proto = Doc.GetProto(anchor);
         proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
         proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index d74da9748..dd03b9b99 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -103,7 +103,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                 <>
                     <DocumentView
                         ref={r => {
-                            whichDoc !== targetDoc && r?.focus(whichDoc, {});
+                            whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true });
                         }}
                         {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
                         isContentActive={returnFalse}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 84cacd919..dc468cf89 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -51,7 +51,7 @@ import { LinkAnchorBox } from './LinkAnchorBox';
 import { LinkDocPreview } from './LinkDocPreview';
 import { RadialMenu } from './RadialMenu';
 import { ScriptingBox } from './ScriptingBox';
-import { PresBox } from './trails/PresBox';
+import { PinProps, PresBox } from './trails/PresBox';
 import React = require('react');
 const { Howl } = require('howler');
 
@@ -144,7 +144,7 @@ export interface DocumentViewSharedProps {
     addDocument?: (doc: Doc | Doc[]) => boolean;
     removeDocument?: (doc: Doc | Doc[]) => boolean;
     moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
-    pinToPres: (document: Doc) => void;
+    pinToPres: (document: Doc, pinProps: PinProps) => void;
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
     canEmbedOnDrag?: boolean;
@@ -486,7 +486,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 selected: -1,
             });
         // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 });
-        RadialMenu.Instance.addItem({ description: 'Pin', event: () => this.props.pinToPres(this.props.Document), icon: 'map-pin', selected: -1 });
+        RadialMenu.Instance.addItem({ description: 'Pin', event: () => this.props.pinToPres(this.props.Document, {}), icon: 'map-pin', selected: -1 });
         RadialMenu.Instance.addItem({ description: 'Open', event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: 'trash', selected: -1 });
 
         SelectionManager.DeselectAll();
@@ -545,7 +545,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             .forEach(spec => (this.layoutDoc[spec.replace(ViewSpecPrefix, '')] = (field => (field instanceof ObjectField ? ObjectField.MakeCopy(field) : field))(anchor[spec])));
         // after a render the general viewSpec should have created the right _componentView, so after a timeout, call the componentview to update its specific view specs
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
-        const focusSpeed = this._componentView?.scrollFocus?.(anchor, options?.instant === false || !LinkDocPreview.LinkInfo);
+        const focusSpeed = this._componentView?.scrollFocus?.(anchor, options?.instant !== true && !LinkDocPreview.LinkInfo);
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options,
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 76ba7765c..2e594d96a 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -30,6 +30,8 @@ import { FaceRectangles } from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import './ImageBox.scss';
 import React = require('react');
+import { PresBox } from './trails';
+import { DocumentViewProps } from './DocumentView';
 
 export const pageSchema = createSchema({
     googlePhotosUrl: 'string',
@@ -49,23 +51,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return FieldView.LayoutString(ImageBox, fieldKey);
     }
     private _dropDisposer?: DragManager.DragDropDisposer;
-    private _transitioning: any; // timer for setting view spec properties
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     @observable _curSuffix = '';
     @observable _uploadIcon = uploadIcons.idle;
-    @observable _focusViewScale: number | undefined = 1;
-    @observable _focusPanX: number | undefined = 0;
-    @observable _focusPanY: number | undefined = 0;
-    get viewScale() {
-        return this._focusViewScale || StrCast(this.layoutDoc._viewScale);
-    }
-    get panX() {
-        return this._focusPanX || StrCast(this.layoutDoc._panX);
-    }
-    get panY() {
-        return this._focusPanY || StrCast(this.layoutDoc._panY);
-    }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
@@ -73,33 +62,30 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     };
 
     @action
-    setViewSpec = (anchor: Doc, preview: boolean) => {
-        if (preview && anchor._viewScale !== undefined) {
-            this._focusViewScale = Cast(anchor._viewScale, 'number', null);
-            this._focusPanX = Cast(anchor._panX, 'number', null);
-            this._focusPanY = Cast(anchor._panX, 'number', null);
-        } else if (anchor._viewScale !== undefined) {
-            const smoothTime = NumCast(anchor.viewTransitionTime);
-            this.layoutDoc.viewTransition = `all ${smoothTime}ms`;
-            this.layoutDoc._panX = NumCast(anchor._panX, NumCast(this.layoutDoc._panY));
-            this.layoutDoc._panY = NumCast(anchor._panY, NumCast(this.layoutDoc._panX));
-            this.layoutDoc._viewScale = NumCast(anchor._viewScale, NumCast(this.layoutDoc._viewScale));
-            this.layoutDoc[this.fieldKey + '-useAlt'] = Cast(anchor._useAlt, 'boolean', null);
-            if (anchor.type === DocumentType.MARKER) {
-                this.dataDoc[this.annotationKey] = new List<Doc>(DocListCast(anchor._annotations));
-            }
-            clearTimeout(this._transitioning);
-            this._transitioning = setTimeout(() => (this.layoutDoc.viewTransition = undefined), smoothTime);
-        }
+    scrollFocus = (anchor: Doc, smooth: boolean) => {
+        let focusSpeed: Opt<number>;
+        PresBox.restoreTargetDocView(
+            this.rootDoc, //
+            { pinDocLayout: BoolCast(anchor.presPinDocLayout) },
+            anchor,
+            (focusSpeed = !smooth ? 0 : NumCast(anchor.presTransition)),
+            !anchor.presPinData
+                ? {}
+                : {
+                      pannable: true,
+                      dataannos: anchor.presAnnotations !== undefined,
+                      dataview: true,
+                  }
+        );
+        return focusSpeed;
     }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
     getAnchor = () => {
         const anchor =
             this._getAnchor?.(this._savedAnnotations) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
-            Docs.Create.ImageanchorDocument({ viewTransitionTime: 1000, unrendered: true, annotationOn: this.rootDoc, _viewScale: NumCast(this.layoutDoc._viewScale, 1), _panX: NumCast(this.layoutDoc._panX), _panY: NumCast(this.layoutDoc._panY) });
+            Docs.Create.ImageanchorDocument({ presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
         if (anchor) {
-            anchor._useAlt = Cast(this.layoutDoc[this.fieldKey + '-useAlt'], 'boolean', null);
-            anchor._annotations = new List<Doc>(DocListCast(this.dataDoc[this.annotationKey]));
+            PresBox.pinDocView(anchor, { pinData: { pannable: true, dataview: true, dataannos: true } }, this.rootDoc);
             this.addDocument(anchor);
             return anchor;
         }
@@ -130,7 +116,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     componentWillUnmount() {
-        clearTimeout(this._transitioning);
         Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
@@ -425,6 +410,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.props.select(false);
     };
     savedAnnotations = () => this._savedAnnotations;
+    styleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
+        if (property === StyleProp.BoxShadow) return undefined;
+        return this.props.styleProvider?.(doc, props, property);
+    };
 
     render() {
         TraceMobx();
@@ -445,6 +434,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                     renderDepth={this.props.renderDepth + 1}
                     fieldKey={this.annotationKey}
+                    styleProvider={this.styleProvider}
                     CollectionView={undefined}
                     isAnnotationOverlay={true}
                     annotationLayerHostsContent={true}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fcb3ccb07..39e323247 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -5,7 +5,7 @@ import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField, PdfField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -27,6 +27,7 @@ import { ImageBox } from './ImageBox';
 import './PDFBox.scss';
 import { VideoBox } from './VideoBox';
 import React = require('react');
+import { PresBox } from './trails';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -207,16 +208,19 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         this._initialScrollTarget = doc;
-        return this._pdfViewer?.scrollFocus(doc, smooth) ?? (didToggle ? 1 : undefined);
+        PresBox.restoreTargetDocView(this.rootDoc, {}, doc, NumCast(doc.presTransition, NumCast(doc.focusSpeed, 500)), { pannable: doc.presPinData ? true : false });
+        return this._pdfViewer?.scrollFocus(doc, NumCast(doc.presPinViewScroll, NumCast(doc.y)), smooth) ?? (didToggle ? 1 : undefined);
     };
     getAnchor = () => {
-        const anchor =
-            this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ??
-            Docs.Create.TextanchorDocument({
+        const docAnchor = () => {
+            const anchor = Docs.Create.TextanchorDocument({
                 title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
-                y: NumCast(this.layoutDoc._scrollTop),
                 unrendered: true,
             });
+            PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
+            return anchor;
+        };
+        const anchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ?? docAnchor();
         this.addDocument(anchor);
         return anchor;
     };
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2984feba5..fdd61463d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -686,7 +686,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     @undoBatch
-    pinToPres = (anchor: Doc) => this.props.pinToPres(anchor);
+    pinToPres = (anchor: Doc) => this.props.pinToPres(anchor, {});
 
     @undoBatch
     makePushpin = (anchor: Doc) => (anchor.isPushpin = !anchor.isPushpin);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 8d805c663..10f2dc016 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -12,7 +12,8 @@ import { List } from '../../../../fields/List';
 import { ObjectField } from '../../../../fields/ObjectField';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents, StopEvent } from '../../../../Utils';
+import { AudioField } from '../../../../fields/URLField';
+import { emptyFunction, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs } from '../../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -33,6 +34,7 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { ScriptingBox } from '../ScriptingBox';
 import './PresBox.scss';
 import { PresEffect, PresMovement, PresStatus } from './PresEnums';
+const { Howl } = require('howler');
 
 export interface PinProps {
     audioRange?: boolean;
@@ -41,6 +43,17 @@ export interface PinProps {
     pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected)
     pinDocLayout?: boolean; // pin layout info (width/height/x/y)
     pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text)
+    pinAudioPlay?: boolean; // pin audio annotation
+    pinData?: {
+        scrollable?: boolean | undefined;
+        pannable?: boolean | undefined;
+        temporal?: boolean | undefined;
+        clippable?: boolean | undefined;
+        dataview?: boolean | undefined;
+        textview?: boolean | undefined;
+        poslayoutview?: boolean | undefined;
+        dataannos?: boolean | undefined;
+    };
 }
 
 @observer
@@ -333,26 +346,37 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.onHideDocument(); //Handles hide after/before
         }
     });
-    static pinDataTypes(target: Doc) {
+    static pinDataTypes(target: Doc): { scrollable?: boolean; pannable?: boolean; temporal?: boolean; clippable?: boolean; dataview?: boolean; textview?: boolean; poslayoutview?: boolean; dataannos?: boolean } {
         const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(target.type as any) || target._viewType === CollectionViewType.Stacking;
-        const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform);
+        const pannable = [DocumentType.IMG, DocumentType.PDF].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform);
         const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any);
         const clippable = [DocumentType.COMPARISON].includes(target.type as any);
         const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG].includes(target.type as any) && target.activeFrame === undefined;
         const poslayoutview = [DocumentType.COL].includes(target.type as any) && target.activeFrame === undefined;
         const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined;
-        return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview };
+        const dataannos = false;
+        return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview, dataannos };
     }
 
     @action
-    static restoreTargetDocView(bestTarget: Doc, activeItem: Doc) {
-        const transTime = NumCast(activeItem.presTransition, 500);
+    playAnnotation = (anno: AudioField) => {};
+    @action
+    static restoreTargetDocView(bestTarget: Doc, pinProps: PinProps | undefined, activeItem: Doc, transTime: number, pinDataTypes = this.pinDataTypes(bestTarget)) {
         const presTransitionTime = `all ${transTime}ms`;
-        const { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview } = this.pinDataTypes(bestTarget);
         bestTarget._viewTransition = presTransitionTime;
-        if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
-        if (temporal) bestTarget._currentTimecode = activeItem.presStartTime;
-        if (scrollable) {
+        if (pinProps?.pinDocLayout) {
+            const transTime = NumCast(activeItem.presTransition, 500);
+            bestTarget._dataTransition = `all ${transTime}ms`;
+            bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x));
+            bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y));
+            bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation));
+            bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
+            bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+            setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
+        }
+        if (pinDataTypes.clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
+        if (pinDataTypes.temporal) bestTarget._currentTimecode = activeItem.presStartTime;
+        if (pinDataTypes.scrollable) {
             bestTarget._scrollTop = activeItem.presPinViewScroll;
             const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
             if (contentBounds) {
@@ -360,13 +384,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
             }
         }
-        if (dataview && activeItem.presData !== undefined) {
+        if (pinDataTypes.dataannos) {
+            const fkey = Doc.LayoutFieldKey(bestTarget);
+            Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>(DocListCast(activeItem.presAnnotations));
+        }
+        if (pinDataTypes.dataview && activeItem.presData !== undefined) {
             const fkey = Doc.LayoutFieldKey(bestTarget);
             Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
             bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt;
         }
-        if (textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
-        if (poslayoutview) {
+        if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+        if (pinDataTypes.poslayoutview) {
             StrListCast(activeItem.presPinLayoutData)
                 .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
                 .forEach(data => {
@@ -385,7 +413,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 transTime + 10
             );
         }
-        if (pannable) {
+        if (pinDataTypes.pannable) {
             const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
             if (contentBounds) {
                 const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
@@ -410,9 +438,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /// reserved fields on the pinDoc so that those values can be restored to the
     /// target doc when navigating to it.
     @action
-    static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined, targetDoc: Doc) {
-        const { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview } = this.pinDataTypes(pinDoc);
-        if (pinProps?.pinDocLayout) {
+    static pinDocView(pinDoc: Doc, pinProps: PinProps, targetDoc: Doc) {
+        if (pinProps.pinDocLayout) {
             pinDoc.presPinLayout = true;
             pinDoc.presX = NumCast(targetDoc.x);
             pinDoc.presY = NumCast(targetDoc.y);
@@ -420,33 +447,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             pinDoc.presWidth = NumCast(targetDoc.width);
             pinDoc.presHeight = NumCast(targetDoc.height);
         }
-        if (pinProps?.pinDocContent) {
-            pinDoc.presPinData = scrollable || temporal || pannable || clippable || dataview || textview || poslayoutview || pinProps.activeFrame !== undefined;
-            if (dataview) {
+        if (pinProps.pinAudioPlay) pinDoc.followLinkAudio = true;
+        if (pinProps.pinData) {
+            pinDoc.presPinData =
+                pinProps.pinData.scrollable ||
+                pinProps.pinData.temporal ||
+                pinProps.pinData.pannable ||
+                pinProps.pinData.clippable ||
+                pinProps.pinData.dataview ||
+                pinProps.pinData.textview ||
+                pinProps.pinData.poslayoutview ||
+                pinProps?.activeFrame !== undefined;
+            if (pinProps.pinData.dataview) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
                 pinDoc.presUseAlt = targetDoc[fkey + '-useAlt'];
                 pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
             }
-            if (textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text;
-            if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop;
-            if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth;
-            if (poslayoutview) pinDoc.presPinLayoutData = new List<string>(DocListCast(pinDoc.presData).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) })));
-            if (pannable) {
-                pinDoc.presPinViewX = NumCast(pinDoc._panX);
-                pinDoc.presPinViewY = NumCast(pinDoc._panY);
-                pinDoc.presPinViewScale = NumCast(pinDoc._viewScale, 1);
+            if (pinProps.pinData.dataannos) {
+                const fkey = Doc.LayoutFieldKey(targetDoc);
+                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']));
+            }
+            if (pinProps.pinData.textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text;
+            if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop;
+            if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth;
+            if (pinProps.pinData.poslayoutview) pinDoc.presPinLayoutData = new List<string>(DocListCast(targetDoc.presData).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) })));
+            if (pinProps.pinData.pannable) {
+                pinDoc.presPinViewX = NumCast(targetDoc._panX);
+                pinDoc.presPinViewY = NumCast(targetDoc._panY);
+                pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
             }
-            if (temporal) {
-                pinDoc.presStartTime = pinDoc._currentTimecode;
-                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(pinDoc.presStartTime) + 0.1);
-                pinDoc.presEndTime = NumCast(pinDoc.clipEnd, duration);
+            if (pinProps.pinData.temporal) {
+                pinDoc.presStartTime = targetDoc._currentTimecode;
+                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(targetDoc.presStartTime) + 0.1);
+                pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration);
             }
         }
         if (pinProps?.pinViewport) {
             // If pinWithView option set then update scale and x / y props of slide
             const bounds = pinProps.pinViewport;
             pinDoc.presPinView = true;
-            pinDoc.presPinViewScale = NumCast(pinDoc._viewScale, 1);
+            pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1);
             pinDoc.presPinViewX = bounds.left + bounds.width / 2;
             pinDoc.presPinViewY = bounds.top + bounds.height / 2;
             pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
@@ -507,35 +547,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     static NavigateToTarget(targetDoc: Doc, activeItem: Doc, openInTab: any, srcContext: Doc, finished?: () => void) {
-        if ((activeItem.presPinLayout || activeItem.presPinView) && DocCast(targetDoc.context)?._currentFrame === undefined) {
-            const transTime = NumCast(activeItem.presTransition, 500);
-            targetDoc._dataTransition = `all ${transTime}ms`;
-            targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x));
-            targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y));
-            targetDoc.rotation = NumCast(activeItem.presRot, NumCast(targetDoc.rotation));
-            targetDoc.width = NumCast(activeItem.presWidth, NumCast(targetDoc.width));
-            targetDoc.height = NumCast(activeItem.presHeight, NumCast(targetDoc.height));
-            setTimeout(() => (targetDoc._dataTransition = undefined), transTime + 10);
-        }
         // If openDocument is selected then it should open the document for the user
         if (activeItem.openDocument) {
             LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc);
         } else if (targetDoc && activeItem.presMovement !== PresMovement.None) {
             LightboxView.SetLightboxDoc(undefined);
             const zooming = activeItem.presMovement !== PresMovement.Pan;
-            DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, finished, undefined, true, NumCast(activeItem.presZoom, 1));
+            DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, activeItem, finished, undefined, true, NumCast(activeItem.presZoom, 1));
         } else if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
             (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
         }
         // After navigating to the document, if it is added as a presPinView then it will
         // adjust the pan and scale to that of the pinView when it was added.
-        if (activeItem.presPinData || activeItem.presPinView) {
+        const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined;
+        if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) {
             clearTimeout(PresBox._navTimer);
             // targetDoc may or may not be displayed.  this gets the first available document (or alias) view that matches targetDoc
             const bestTargetView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
-            const bestTarget = bestTargetView?.props.Document;
-            if (bestTarget) PresBox._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem);
-            activeItem.presPinAudioPlay && bestTargetView?.docView?.playAnnotation();
+            if (bestTargetView?.props.Document) PresBox._navTimer = PresBox.restoreTargetDocView(bestTargetView?.props.Document, { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500));
         }
     }
 
@@ -747,7 +776,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             } else {
                 if (!doc.aliasOf) {
                     const original = Doc.MakeAlias(doc);
-                    TabDocView.PinDoc(original);
+                    TabDocView.PinDoc(original, {});
                     setTimeout(() => this.removeDocument(doc), 0);
                     return false;
                 } else {
@@ -1316,7 +1345,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             Effects
                             <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                                 <div className="presBox-subheading">Play Audio Annotation</div>
-                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPinAudioPlay = !BoolCast(activeItem.presPinAudioPlay))} checked={BoolCast(activeItem.presPinAudioPlay)} />
+                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.followLinkAudio = !BoolCast(activeItem.followLinkAudio))} checked={BoolCast(activeItem.followLinkAudio)} />
                             </div>
                             <div
                                 className="presBox-dropdown"
@@ -1694,7 +1723,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const presData = Cast(this.rootDoc.data, listSpec(Doc));
             if (data && presData) {
                 data.push(doc);
-                TabDocView.PinDoc(doc);
+                TabDocView.PinDoc(doc, {});
                 this.gotoDocument(this.childDocs.length, this.activeItem);
             } else {
                 this.props.addDocTab(doc, 'add:right');
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 9af0949eb..133b882f6 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -52,7 +52,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     };
 
     @undoBatch
-    pinToPres = () => this.props.pinToPres(this.annoTextRegion);
+    pinToPres = () => this.props.pinToPres(this.annoTextRegion, {});
 
     @undoBatch
     makePushpin = () => (this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6ff87ef9f..5a5c63c3d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -166,12 +166,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     // scrolls to focus on a nested annotation document.  if this is part a link preview then it will jump to the scroll location,
     // otherwise it will scroll smoothly.
-    scrollFocus = (doc: Doc, smooth: boolean) => {
+    scrollFocus = (doc: Doc, scrollTop: number, smooth: boolean) => {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight));
+            const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight));
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 if (!this._pdfViewer) this._initialScroll = scrollTo;
                 else if (smooth) smoothScroll((focusSpeed = NumCast(doc.focusSpeed, 500)), mainCont, scrollTo);
@@ -203,6 +203,11 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
         document.removeEventListener('pagesinit', this.pagesinit);
         var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : '';
+        this._disposers.scale = reaction(
+            () => NumCast(this.props.layoutDoc._viewScale, 1),
+            scale => (this._pdfViewer.currentScaleValue = scale),
+            { fireImmediately: true }
+        );
         this._disposers.scroll = reaction(
             () => Math.abs(NumCast(this.props.Document._scrollTop)),
             pos => {
@@ -290,7 +295,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @action
     scrollToAnnotation = (scrollToAnnotation: Doc) => {
         if (scrollToAnnotation) {
-            this.scrollFocus(scrollToAnnotation, true);
+            this.scrollFocus(scrollToAnnotation, NumCast(scrollToAnnotation.y), true);
             Doc.linkFollowHighlight(scrollToAnnotation);
         }
     };
-- 
cgit v1.2.3-70-g09d2


From 30e7fc1b2cb4b5c5f8d5f5e4f808b91e69629245 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Nov 2022 11:00:06 -0500
Subject: fixed pushpin behaviors by not animating when anchor viewspec already
 matched document.  fixed recording button highlighting. switched LinkEditor
 to edit properties of destination insteqad of source anchor

---
 src/client/views/DocumentButtonBar.tsx             | 22 +++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 11 ++--
 src/client/views/linking/LinkEditor.tsx            | 14 ++---
 src/client/views/nodes/ImageBox.tsx                | 11 ++--
 src/client/views/nodes/trails/PresBox.tsx          | 72 +++++++++++++++-------
 5 files changed, 80 insertions(+), 50 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 681349ccf..ecf330792 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -386,18 +386,18 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                     style={{ backgroundColor: this._isRecording ? Colors.ERROR_RED : Colors.DARK_GRAY, color: Colors.WHITE }}
                     onPointerDown={action((e: React.PointerEvent) => {
                         this._isRecording = true;
-                        this.props.views().map(
-                            view =>
-                                view &&
-                                DocumentViewInternal.recordAudioAnnotation(
-                                    view.dataDoc,
-                                    view.LayoutFieldKey,
-                                    stopFunc => (this._stopFunc = stopFunc),
-                                    action(() => (this._isRecording = false))
-                                )
-                        );
+                        this.props.views().map(view => view && DocumentViewInternal.recordAudioAnnotation(view.dataDoc, view.LayoutFieldKey, stopFunc => (this._stopFunc = stopFunc), emptyFunction));
                         const b = UndoManager.StartBatch('Recording');
-                        setupMoveUpEvents(this, e, returnFalse, () => this._stopFunc(), emptyFunction);
+                        setupMoveUpEvents(
+                            this,
+                            e,
+                            returnFalse,
+                            action(() => {
+                                this._isRecording = false;
+                                this._stopFunc();
+                            }),
+                            emptyFunction
+                        );
                     })}>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="microphone" />
                 </div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8a97797c7..57cccec4a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1532,17 +1532,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     scrollFocus = (anchor: Doc, smooth: boolean) => {
-        let focusSpeed: Opt<number>;
-        PresBox.restoreTargetDocView(
+        const focusSpeed = !smooth ? 0 : NumCast(anchor.presTransition);
+        return PresBox.restoreTargetDocView(
             this.rootDoc, //
             { pinDocLayout: BoolCast(anchor.presPinDocLayout) },
             anchor,
-            (focusSpeed = !smooth ? 0 : NumCast(anchor.presTransition)),
+            focusSpeed,
             {
                 pannable: anchor.presPinData ? true : false,
             }
-        );
-        return focusSpeed;
+        )
+            ? focusSpeed
+            : undefined;
     }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
     getAnchor = () => {
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index d90a91ab7..8c4d756d2 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -297,13 +297,13 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
         );
     }
 
-    @computed get sourceAnchor() {
+    @computed get destinationAnchor() {
         const ldoc = this.props.linkDoc;
         if (this.props.sourceDoc !== ldoc.anchor1 && this.props.sourceDoc !== ldoc.anchor2) {
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor1);
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor2);
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor2);
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor1);
         }
-        return this.props.sourceDoc;
+        return LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc) ?? this.props.sourceDoc;
     }
     @action
     changeEffectDropdown = () => {
@@ -313,7 +313,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     @undoBatch
     changeEffect = action((follow: string) => {
         this.openEffectDropdown = false;
-        this.sourceAnchor.presEffect = follow;
+        this.destinationAnchor.presEffect = follow;
     });
 
     @computed
@@ -323,7 +323,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                 <div className="linkEditor-followingDropdown-label">Transition Effect:</div>
                 <div className="linkEditor-followingDropdown-dropdown">
                     <div className="linkEditor-followingDropdown-header" onPointerDown={this.changeEffectDropdown}>
-                        {StrCast(this.sourceAnchor.presEffect, 'default')}
+                        {StrCast(this.destinationAnchor.presEffect, 'default')}
                         <FontAwesomeIcon className="linkEditor-followingDropdown-icon" icon={this.openEffectDropdown ? 'chevron-up' : 'chevron-down'} size={'lg'} />
                     </div>
                     <div className="linkEditor-followingDropdown-optionsList" style={{ display: this.openEffectDropdown ? '' : 'none' }}>
@@ -447,7 +447,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                 </div>
                 {this.followingDropdown}
                 {this.effectDropdown}
-                {PresBox.inputter('0.1', '0.1', '10', NumCast(this.sourceAnchor.presTransition) / 1000, true, (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => (this.sourceAnchor.presTransition = timeInMS)))}
+                {PresBox.inputter('0.1', '0.1', '10', NumCast(this.destinationAnchor.presTransition) / 1000, true, (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => (this.destinationAnchor.presTransition = timeInMS)))}
                 <div
                     className={'slider-headers'}
                     style={{
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4092a2575..1d79febdf 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -63,12 +63,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @action
     scrollFocus = (anchor: Doc, smooth: boolean) => {
-        let focusSpeed: Opt<number>;
-        PresBox.restoreTargetDocView(
+        const focusSpeed = !smooth ? 0 : NumCast(anchor.presTransition);
+        return PresBox.restoreTargetDocView(
             this.rootDoc, //
             { pinDocLayout: BoolCast(anchor.presPinDocLayout) },
             anchor,
-            (focusSpeed = !smooth ? 0 : NumCast(anchor.presTransition)),
+            focusSpeed,
             !anchor.presPinData
                 ? {}
                 : {
@@ -76,8 +76,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                       dataannos: anchor.presAnnotations !== undefined,
                       dataview: true,
                   }
-        );
-        return focusSpeed;
+        )
+            ? focusSpeed
+            : undefined;
     }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
     getAnchor = () => {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 7235481e0..e19b53f50 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -365,25 +365,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     static restoreTargetDocView(bestTarget: Doc, pinProps: PinProps | undefined, activeItem: Doc, transTime: number, pinDataTypes = this.pinDataTypes(bestTarget)) {
         const presTransitionTime = `all ${transTime}ms`;
-        bestTarget._viewTransition = presTransitionTime;
+        let changed = false;
         if (pinProps?.pinDocLayout) {
-            const transTime = NumCast(activeItem.presTransition, 500);
-            bestTarget._dataTransition = `all ${transTime}ms`;
-            bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x));
-            bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y));
-            bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation));
-            bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
-            bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
-            setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
+            if (
+                bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) ||
+                bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) ||
+                bestTarget.rotation !== NumCast(activeItem.presRot, NumCast(bestTarget.rotation)) ||
+                bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) ||
+                bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height))
+            ) {
+                bestTarget._dataTransition = `all ${transTime}ms`;
+                bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x));
+                bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y));
+                bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation));
+                bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width));
+                bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height));
+                setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
+                changed = true;
+            }
+        }
+        if (pinDataTypes.clippable) {
+            if (bestTarget._clipWidth !== activeItem.presPinClipWidth) {
+                bestTarget._clipWidth = activeItem.presPinClipWidth;
+                changed = true;
+            }
+        }
+        if (pinDataTypes.temporal) {
+            if (bestTarget._currentTimecode !== activeItem.presStartTime) {
+                bestTarget._currentTimecode = activeItem.presStartTime;
+                changed = true;
+            }
         }
-        if (pinDataTypes.clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
-        if (pinDataTypes.temporal) bestTarget._currentTimecode = activeItem.presStartTime;
         if (pinDataTypes.scrollable) {
-            bestTarget._scrollTop = activeItem.presPinViewScroll;
-            const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
-            if (contentBounds) {
-                const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
-                dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
+            if (bestTarget._scrollTop !== activeItem.presPinViewScroll) {
+                bestTarget._scrollTop = activeItem.presPinViewScroll;
+                changed = true;
+                const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
+                if (contentBounds) {
+                    const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
+                    dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
+                }
             }
         }
         if (pinDataTypes.dataannos) {
@@ -397,6 +418,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
         if (pinDataTypes.poslayoutview) {
+            changed = true;
             StrListCast(activeItem.presPinLayoutData)
                 .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
                 .forEach(data => {
@@ -428,12 +450,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     dv.ComponentView?.brushView?.(viewport);
                 }
             } else {
-                bestTarget._panX = activeItem.presPinViewX;
-                bestTarget._panY = activeItem.presPinViewY;
-                bestTarget._viewScale = activeItem.presPinViewScale;
+                if (bestTarget._panX !== activeItem.presPinViewX || bestTarget._panY !== activeItem.presPinViewY || bestTarget._viewScale !== activeItem.presPinViewScale) {
+                    bestTarget._panX = activeItem.presPinViewX;
+                    bestTarget._panY = activeItem.presPinViewY;
+                    bestTarget._viewScale = activeItem.presPinViewScale;
+                    changed = true;
+                }
             }
         }
-        return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10);
+        if (changed) {
+            bestTarget._viewTransition = presTransitionTime;
+            return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10);
+        }
     }
 
     /// copies values from the targetDoc (which is the prototype of the pinDoc) to
@@ -495,7 +523,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     }
 
-    static _navTimer: NodeJS.Timeout;
+    static _navTimer: NodeJS.Timeout | undefined;
     /**
      * This method makes sure that cursor navigates to the element that
      * has the option open and last in the group.
@@ -563,7 +591,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // adjust the pan and scale to that of the pinView when it was added.
         const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined;
         if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) {
-            clearTimeout(PresBox._navTimer);
+            PresBox._navTimer && clearTimeout(PresBox._navTimer);
             // targetDoc may or may not be displayed.  this gets the first available document (or alias) view that matches targetDoc
             const bestTargetView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
             if (bestTargetView?.props.Document) PresBox._navTimer = PresBox.restoreTargetDocView(bestTargetView?.props.Document, { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500));
-- 
cgit v1.2.3-70-g09d2


From cdd1dd95c67fcb95914913bb5346c5117c0abc27 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 18 Nov 2022 17:26:17 -0500
Subject: migration of link properties to propertiesView

---
 src/client/util/LinkFollower.ts                    |  14 +-
 src/client/util/LinkManager.ts                     |   3 +-
 src/client/util/SelectionManager.ts                |   4 +
 src/client/views/DocumentButtonBar.tsx             |   4 +-
 src/client/views/PropertiesView.scss               |   8 +-
 src/client/views/PropertiesView.tsx                | 307 +++++++++++++++------
 .../CollectionFreeFormLinkView.tsx                 |  27 +-
 src/client/views/linking/LinkEditor.tsx            |  16 +-
 src/client/views/linking/LinkMenu.scss             |   8 +-
 src/client/views/linking/LinkMenuItem.scss         |  24 +-
 src/client/views/linking/LinkMenuItem.tsx          |  42 ++-
 src/client/views/nodes/LinkDocPreview.tsx          |   2 +
 src/client/views/nodes/trails/PresBox.tsx          |  30 +-
 src/client/views/nodes/trails/PresEnums.ts         |   3 +
 src/fields/Doc.ts                                  |   2 +-
 src/fields/documentSchemas.ts                      |   2 +-
 16 files changed, 327 insertions(+), 169 deletions(-)

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

diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index a3eb7ed7a..52885e428 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -111,7 +111,19 @@ export class LinkFollower {
                             containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
                         }
                         const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'inPlace'), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
+                        DocumentManager.Instance.jumpToDocument(
+                            target,
+                            zoom,
+                            (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'inPlace'), finished),
+                            targetContexts,
+                            linkDoc,
+                            undefined,
+                            sourceDoc,
+                            allFinished,
+                            undefined,
+                            undefined,
+                            Cast(target.presZoom, 'number', null)
+                        );
                     }
                 } else {
                     allFinished();
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 7a12a8580..49cc3218d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -19,7 +19,8 @@ import { Cast, StrCast } from '../../fields/Types';
 export class LinkManager {
     @observable static _instance: LinkManager;
     @observable static userLinkDBs: Doc[] = [];
-    public static currentLink: Opt<Doc>;
+    @observable public static currentLink: Opt<Doc>;
+    @observable public static currentLinkAnchor: Opt<Doc>;
     public static get Instance() {
         return LinkManager._instance;
     }
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index a3d6f5227..1a2dda953 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -4,6 +4,7 @@ import { Doc, Opt } from '../../fields/Doc';
 import { DocCast } from '../../fields/Types';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocumentView } from '../views/nodes/DocumentView';
+import { LinkManager } from './LinkManager';
 import { ScriptingGlobals } from './ScriptingGlobals';
 
 export namespace SelectionManager {
@@ -15,6 +16,7 @@ export namespace SelectionManager {
         @action
         SelectSchemaViewDoc(doc: Opt<Doc>) {
             manager.SelectedSchemaDocument = doc;
+            if (doc?.type === DocumentType.LINK) LinkManager.currentLink = doc;
         }
         @action
         SelectView(docView: DocumentView, ctrlPressed: boolean): void {
@@ -32,6 +34,7 @@ export namespace SelectionManager {
                 manager.SelectedViews.clear();
                 manager.SelectedViews.set(docView, docView.rootDoc);
             }
+            if (docView.rootDoc.type === DocumentType.LINK) LinkManager.currentLink = docView.rootDoc;
         }
         @action
         DeselectView(docView: DocumentView): void {
@@ -42,6 +45,7 @@ export namespace SelectionManager {
         }
         @action
         DeselectAll(): void {
+            LinkManager.currentLink = undefined;
             manager.SelectedSchemaDocument = undefined;
             Array.from(manager.SelectedViews.keys()).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
             manager.SelectedViews.clear();
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ecf330792..5969d55e9 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -21,7 +21,7 @@ import './DocumentButtonBar.scss';
 import { Colors } from './global/globalEnums';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhereMod } from './nodes/DocumentView';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
 import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
 import { TemplateMenu } from './TemplateMenu';
@@ -180,7 +180,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                                 googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                                 dataDoc.googleDoc = googleDoc;
                             }
-                            CollectionDockingView.AddSplit(googleDoc, 'right');
+                            CollectionDockingView.AddSplit(googleDoc, OpenWhereMod.right);
                         } else if (e.altKey) {
                             e.preventDefault();
                             window.open(googleDocUrl);
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 437df4739..30806f718 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -1,9 +1,9 @@
-@import "./global/globalCssVariables.scss";
+@import './global/globalCssVariables.scss';
 
 .propertiesView {
     height: 100%;
     width: 250;
-    font-family: "Roboto";
+    font-family: 'Roboto';
     cursor: auto;
 
     overflow-x: hidden;
@@ -843,7 +843,7 @@
 }
 
 .propertiesView-section {
-    padding: 10px 0;
+    padding-left: 20px;
 }
 
 .propertiesView-input {
@@ -877,4 +877,4 @@
     color: white;
     padding-left: 8px;
     background-color: rgb(51, 51, 51);
-}
\ No newline at end of file
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 93a3fd253..076dc8261 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -4,7 +4,7 @@ import { faAnchor, faArrowRight } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Tooltip } from '@material-ui/core';
 import { intersection } from 'lodash';
-import { action, autorun, computed, Lambda, observable } from 'mobx';
+import { action, autorun, computed, Lambda, observable, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
@@ -27,12 +27,13 @@ import { InkStrokeProperties } from './InkStrokeProperties';
 import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
 import { FilterBox } from './nodes/FilterBox';
 import { KeyValueBox } from './nodes/KeyValueBox';
-import { PresBox } from './nodes/trails';
+import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 import { PropertiesButtons } from './PropertiesButtons';
 import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { Colors } from './global/globalEnums';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -54,7 +55,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get selectedDoc() {
-        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || Doc.ActiveDashboard;
+        return LinkManager.currentLink || SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || Doc.ActiveDashboard;
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
@@ -405,7 +406,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get expansionIcon() {
         return (
-            <Tooltip title={<div className="dash-tooltip">{'Show more permissions'}</div>}>
+            <Tooltip title={<div className="dash-tooltip">Show more permissions</div>}>
                 <div
                     className="expansion-button"
                     onPointerDown={() => {
@@ -1406,6 +1407,37 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         }
     });
 
+    @undoBatch
+    changeAnimationBehavior = action((behavior: string) => {
+        const lanch = this.destinationAnchor;
+        if (lanch) {
+            lanch.presEffect = behavior;
+        }
+    });
+    @undoBatch
+    @action
+    updateEffectDirection = (effect: PresEffectDirection) => {
+        const lanch = this.destinationAnchor;
+        if (lanch) {
+            lanch.presEffectDirection = effect;
+        }
+    };
+
+    @undoBatch
+    animationDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
+        const lanch = this.destinationAnchor;
+        const color = lanch?.presEffectDirection === direction || (direction === PresEffectDirection.Center && !lanch?.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
+        return (
+            <Tooltip title={<div className="dash-tooltip">{direction}</div>}>
+                <div
+                    style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
+                    onClick={() => this.updateEffectDirection(direction)}>
+                    {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
+                </div>
+            </Tooltip>
+        );
+    };
+
     @undoBatch
     changeFollowBehavior = action((follow: string) => {
         if (LinkManager.currentLink && this.selectedDoc) {
@@ -1438,19 +1470,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         }
     };
 
-    toggleAnchor = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove))));
+    toggleProp = (e: React.PointerEvent, prop: string) => {
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc[prop] = !this.selectedDoc[prop]))));
     };
 
-    toggleArrow = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))));
-    };
+    @computed get destinationAnchor() {
+        const ldoc = LinkManager.currentLink;
+        const lanch = LinkManager.currentLinkAnchor;
+        if (ldoc && lanch) return LinkManager.getOppositeAnchor(ldoc, lanch) ?? lanch;
+        return ldoc ? DocCast(ldoc.anchor2) : ldoc;
+    }
 
-    toggleZoomToTarget1 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor1).followLinkZoom = !DocCast(this.selectedDoc.anchor1).followLinkZoom))));
-    };
-    toggleZoomToTarget2 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor2).followLinkZoom = !DocCast(this.selectedDoc.anchor2).followLinkZoom))));
+    @computed get sourceAnchor() {
+        return LinkManager.currentLinkAnchor ?? (LinkManager.currentLink && this.destinationAnchor ? LinkManager.getOppositeAnchor(LinkManager.currentLink, this.destinationAnchor) : LinkManager.currentLink);
+    }
+
+    toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc) => {
+        anchor && setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (anchor[prop] = !anchor[prop]))));
     };
 
     @computed
@@ -1485,6 +1521,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    // Converts seconds to ms and updates presTransition
+    setZoom = (number: String, change?: number) => {
+        let scale = Number(number) / 100;
+        if (change) scale += change;
+        if (scale < 0.01) scale = 0.01;
+        if (scale > 1) scale = 1;
+        this.destinationAnchor && (this.destinationAnchor.presZoom = scale);
+    };
+    // Converts seconds to ms and updates presTransition
+    setZoomSrc = (number: String, change?: number) => {
+        let scale = Number(number) / 100;
+        if (change) scale += change;
+        if (scale < 0.01) scale = 0.01;
+        if (scale > 1) scale = 1;
+        this.sourceAnchor && (this.sourceAnchor.presZoom = scale);
+    };
+
     /**
      * Handles adding and removing members from the sharing panel
      */
@@ -1498,6 +1551,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     render() {
         const isNovice = Doc.noviceMode;
+        const zoom = Number((NumCast(this.destinationAnchor?.presZoom) * 100).toPrecision(3));
+        const zoomSrc = Number((NumCast(this.sourceAnchor?.presZoom) * 100).toPrecision(3));
+        const targZoom = this.destinationAnchor?.followLinkZoom;
+        const srcZooom = this.sourceAnchor?.followLinkZoom;
         if (!this.selectedDoc && !this.isPres) {
             return (
                 <div className="propertiesView" style={{ width: this.props.width }}>
@@ -1507,81 +1564,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 </div>
             );
         } else {
-            if (this.selectedDoc && this.isLink) {
-                return (
-                    <div className="propertiesView">
-                        <div className="propertiesView-title">Linking</div>
-                        <div className="propertiesView-section">
-                            <p className="propertiesView-label">Information</p>
-                            <div className="propertiesView-input first">
-                                <p>Link Relationship</p>
-                                {this.editRelationship}
-                            </div>
-                            <div className="propertiesView-input">
-                                <p>Description</p>
-                                {this.editDescription}
-                            </div>
-                        </div>
-                        <div className="propertiesView-section">
-                            <p className="propertiesView-label">Behavior</p>
-                            <div className="propertiesView-input inline first">
-                                <p>Follow</p>
-                                <select name="selectList" id="selectList" onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.selectedDoc.followLinkLocation, 'default')}>
-                                    <option value="default">Default</option>
-                                    <option value="add:left">Open in new left pane</option>
-                                    <option value="add:right">Open in new right pane</option>
-                                    <option value="replace:left">Replace left tab</option>
-                                    <option value="replace:right">Replace right tab</option>
-                                    <option value="fullScreen">Open full screen</option>
-                                    <option value="add">Open in new tab</option>
-                                    <option value="replace">Replace current tab</option>
-                                    {this.selectedDoc.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
-                                </select>
-                            </div>
-                            <div className="propertiesView-input inline">
-                                <p>Auto-move anchor</p>
-                                <button
-                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !this.selectedDoc.linkAutoMove ? '' : '#4476f7', borderRadius: 3 }}
-                                    onPointerDown={this.toggleAnchor}
-                                    onClick={e => e.stopPropagation()}
-                                    className="propertiesButton">
-                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                                </button>
-                            </div>
-                            <div className="propertiesView-input inline">
-                                <p>Display arrow</p>
-                                <button
-                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !this.selectedDoc.displayArrow ? '' : '#4476f7', borderRadius: 3 }}
-                                    onPointerDown={this.toggleArrow}
-                                    onClick={e => e.stopPropagation()}
-                                    className="propertiesButton">
-                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                                </button>
-                            </div>
-                            <div className="propertiesView-input inline">
-                                <p>Zoom to target</p>
-                                <button
-                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
-                                    onPointerDown={this.toggleZoomToTarget1}
-                                    onClick={e => e.stopPropagation()}
-                                    className="propertiesButton">
-                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                                </button>
-                            </div>
-                            <div className="propertiesView-input inline">
-                                <p>Zoom to source</p>
-                                <button
-                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
-                                    onPointerDown={this.toggleZoomToTarget2}
-                                    onClick={e => e.stopPropagation()}
-                                    className="propertiesButton">
-                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                                </button>
-                            </div>
-                        </div>
-                    </div>
-                );
-            }
             if (this.selectedDoc && !this.isPres) {
                 return (
                     <div
@@ -1599,6 +1581,153 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         {this.contextsSubMenu}
 
                         {this.linksSubMenu}
+                        {!this.selectedDoc || !LinkManager.currentLink ? null : (
+                            <>
+                                <div className="propertiesView-section">
+                                    <div className="propertiesView-input first">
+                                        <p>Link Relationship</p>
+                                        {this.editRelationship}
+                                    </div>
+                                    <div className="propertiesView-input">
+                                        <p>Description</p>
+                                        {this.editDescription}
+                                    </div>
+                                </div>
+                                <div className="propertiesView-section">
+                                    <div className="propertiesView-input inline first">
+                                        <p>Follow Behavior</p>
+                                        <select onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.selectedDoc.followLinkLocation, 'default')}>
+                                            <option value="default">Default</option>
+                                            <option value="add:left">Open in new left pane</option>
+                                            <option value="add:right">Open in new right pane</option>
+                                            <option value="replace:left">Replace left tab</option>
+                                            <option value="replace:right">Replace right tab</option>
+                                            <option value="fullScreen">Open full screen</option>
+                                            <option value="add">Open in new tab</option>
+                                            <option value="replace">Replace current tab</option>
+                                            <option value="inPlace">Opin in place</option>
+                                            {this.selectedDoc.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+                                        </select>
+                                    </div>
+                                    <div className="propertiesView-input inline first">
+                                        <p>Target Effect</p>
+                                        <select style={{ width: '100%' }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.destinationAnchor?.presEffect, 'default')}>
+                                            <option value="default">Default</option>
+                                            {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
+                                                <option value={effect.toString()}>{effect.toString()}</option>
+                                            ))}
+                                        </select>
+                                        <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40 }}>
+                                            {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+                                            {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+                                            {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+                                            {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+                                            {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+                                        </div>
+                                    </div>
+                                    {PresBox.inputter('0.1', '0.1', '10', NumCast(this.destinationAnchor?.presTransition) / 1000, true, (val: string) =>
+                                        PresBox.SetTransitionTime(val, (timeInMS: number) => this.destinationAnchor && (this.destinationAnchor.presTransition = timeInMS))
+                                    )}{' '}
+                                    <div
+                                        className={'slider-headers'}
+                                        style={{
+                                            display: 'grid',
+                                            justifyContent: 'space-between',
+                                            width: '100%',
+                                            gridTemplateColumns: 'auto auto auto',
+                                            borderTop: 'solid',
+                                        }}>
+                                        <div className="slider-text">Fast</div>
+                                        <div className="slider-text">Medium</div>
+                                        <div className="slider-text">Slow</div>
+                                    </div>{' '}
+                                    <div className="propertiesView-input inline">
+                                        <p>Play Target Audio</p>
+                                        <button
+                                            style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
+                                            onClick={e => e.stopPropagation()}
+                                            className="propertiesButton">
+                                            <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                        </button>
+                                    </div>
+                                    <div className="propertiesView-input inline">
+                                        <p>Auto-move anchor</p>
+                                        <button
+                                            style={{ background: !this.selectedDoc.linkAutoMove ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleProp(e, 'linkAutoMove')}
+                                            onClick={e => e.stopPropagation()}
+                                            className="propertiesButton">
+                                            <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                        </button>
+                                    </div>
+                                    <div className="propertiesView-input inline">
+                                        <p>Show link</p>
+                                        <button
+                                            style={{ background: !this.selectedDoc.linkDisplay ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleProp(e, 'linkDisplay')}
+                                            onClick={e => e.stopPropagation()}
+                                            className="propertiesButton">
+                                            <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                        </button>
+                                    </div>
+                                    <div className="propertiesView-input inline">
+                                        <p>Display arrow</p>
+                                        <button
+                                            style={{ background: !this.selectedDoc.linkDisplayArrow ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleProp(e, 'linkDisplayArrow')}
+                                            onClick={e => e.stopPropagation()}
+                                            className="propertiesButton">
+                                            <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                        </button>
+                                    </div>
+                                    <div className="propertiesView-input inline">
+                                        <p>Zoom to target (% screen filled)</p>
+                                        <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'flex' }}>
+                                            <input className="presBox-input" type="number" value={zoom} />
+                                            <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
+                                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
+                                                    <FontAwesomeIcon icon={'caret-up'} />
+                                                </div>
+                                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
+                                                    <FontAwesomeIcon icon={'caret-down'} />
+                                                </div>
+                                            </div>
+                                        </div>
+                                        <button
+                                            style={{ background: !targZoom ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.destinationAnchor)}
+                                            onClick={e => e.stopPropagation()}
+                                            className="propertiesButton">
+                                            <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                        </button>
+                                    </div>
+                                    {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom)}
+                                    <div className="propertiesView-input inline">
+                                        <p>Zoom to source (% screen filled)</p>
+                                        <div className="ribbon-property" style={{ display: !srcZooom ? 'none' : 'flex' }}>
+                                            <input className="presBox-input" type="number" value={zoomSrc} />
+                                            <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
+                                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoomSrc(String(zoomSrc), 0.1))}>
+                                                    <FontAwesomeIcon icon={'caret-up'} />
+                                                </div>
+                                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoomSrc(String(zoomSrc), -0.1))}>
+                                                    <FontAwesomeIcon icon={'caret-down'} />
+                                                </div>
+                                            </div>
+                                        </div>
+                                        <button
+                                            style={{ background: !srcZooom ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+                                            onClick={e => e.stopPropagation()}
+                                            className="propertiesButton">
+                                            <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                        </button>
+                                    </div>
+                                    {!srcZooom ? null : PresBox.inputter('0', '1', '100', zoomSrc, true, this.setZoomSrc)}
+                                </div>
+                            </>
+                        )}
 
                         {this.inkSubMenu}
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index bf9de6760..a4131d7c0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -44,7 +44,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                 this._start = Date.now();
                 this._timeout && clearTimeout(this._timeout);
                 this._timeout = setTimeout(this.timeout, 25);
-                setTimeout(this.placeAnchors);
+                setTimeout(this.placeAnchors, 10); // when docs are dragged, their transforms will update before a render has been performed.  placeanchors needs to come after a render to find things in the dom.  a 0 timeout will still come before the render
             }),
             { fireImmediately: true }
         );
@@ -163,9 +163,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
 
     @action
     toggleProperties = () => {
-        if (SettingsManager.propertiesWidth > 0) {
-            SettingsManager.propertiesWidth = 0;
-        } else {
+        if ((SettingsManager.propertiesWidth ?? 0) < 100) {
             SettingsManager.propertiesWidth = 250;
         }
     };
@@ -217,14 +215,25 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
 
         const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].linkOffsetX);
         const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].linkOffsetY);
-        return { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13], pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13] };
+        return {
+            a,
+            b,
+            pt1norm,
+            pt2norm,
+            aActive,
+            bActive,
+            textX,
+            textY,
+            pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13],
+            pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13],
+        };
     }
 
     render() {
+        //setTimeout(action(() => (LinkManager.currentLink = this.props.LinkDocs[0])));
         if (!this.renderData) return null;
 
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
-        LinkManager.currentLink = this.props.LinkDocs[0];
         const linkRelationship = Field.toString(LinkManager.currentLink?.linkRelationship as any as Field); //get string representing relationship
         const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
         const linkColorList = Doc.UserDoc().linkColorList as List<string>;
@@ -241,8 +250,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         //thickness varies linearly from 3px to 12px for increasing link count
         const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
 
-        if (this.props.LinkDocs[0].displayArrow === undefined) {
-            this.props.LinkDocs[0].displayArrow = false;
+        if (this.props.LinkDocs[0].linkDisplayArrow === undefined) {
+            this.props.LinkDocs[0].linkDisplayArrow = false;
         }
 
         return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || (!this.props.LinkDocs[0].linkDisplay && !aActive && !bActive) ? null : (
@@ -257,7 +266,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                     style={{ pointerEvents: 'all', opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
                     onClick={this.onClickLine}
                     d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
-                    markerEnd={this.props.LinkDocs[0].displayArrow ? 'url(#arrowhead)' : ''}
+                    markerEnd={this.props.LinkDocs[0].linkDisplayArrow ? 'url(#arrowhead)' : ''}
                 />
                 {textX === undefined ? null : (
                     <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 8c4d756d2..07c20dae4 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -327,21 +327,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                         <FontAwesomeIcon className="linkEditor-followingDropdown-icon" icon={this.openEffectDropdown ? 'chevron-up' : 'chevron-down'} size={'lg'} />
                     </div>
                     <div className="linkEditor-followingDropdown-optionsList" style={{ display: this.openEffectDropdown ? '' : 'none' }}>
-                        {[
-                            PresEffect.None,
-                            PresEffect.Zoom,
-                            PresEffect.Lightspeed,
-                            PresEffect.Fade,
-                            PresEffect.Flip,
-                            PresEffect.Rotate,
-                            PresEffect.Bounce,
-                            PresEffect.Roll,
-                            PresEffect.Left,
-                            PresEffect.Right,
-                            PresEffect.Center,
-                            PresEffect.Top,
-                            PresEffect.Bottom,
-                        ].map(effect => (
+                        {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
                             <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeEffect(effect.toString())}>
                                 {effect.toString()}
                             </div>
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index 77c16a28f..80cf93ed8 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables';
 
 .linkMenu {
     width: auto;
@@ -13,7 +13,6 @@
         border: 1px solid #e4e4e4;
         box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
         background: white;
-        min-width: 170px;
         max-height: 230px;
         overflow-y: scroll;
         z-index: 10;
@@ -22,7 +21,7 @@
     .linkMenu-list {
         white-space: nowrap;
         overflow-x: hidden;
-        width: 240px;
+        width: 100%;
         scrollbar-color: white;
 
         &:last-child {
@@ -39,7 +38,6 @@
     border-bottom: 0.5px solid lightgray;
     //@extend: 5px 0;
 
-
     &:last-child {
         border-bottom: none;
     }
@@ -76,4 +74,4 @@
             display: none;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
index 8333aa374..2ca97a27d 100644
--- a/src/client/views/linking/LinkMenuItem.scss
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -1,7 +1,7 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables';
 
 .linkMenu-item {
-    // border-top: 0.5px solid $medium-gray; 
+    // border-top: 0.5px solid $medium-gray;
     position: relative;
     display: flex;
     border-top: 0.5px solid #cdcdcd;
@@ -14,7 +14,6 @@
 
     background-color: white;
 
-
     .linkMenu-name {
         position: relative;
         width: auto;
@@ -22,9 +21,7 @@
         display: flex;
 
         .linkMenu-text {
-
-            // padding: 4px 2px;
-            //display: inline;
+            width: 100%;
 
             .linkMenu-source-title {
                 text-decoration: none;
@@ -35,9 +32,7 @@
                 margin-left: 20px;
             }
 
-
             .linkMenu-title-wrapper {
-
                 display: flex;
                 align-items: center;
                 min-height: 20px;
@@ -59,7 +54,7 @@
 
                 .linkMenu-destination-title {
                     text-decoration: none;
-                    color: #4476F7;
+                    color: #4476f7;
                     font-size: 13px;
                     line-height: 0.9;
                     padding-bottom: 2px;
@@ -96,9 +91,7 @@
                 //overflow-wrap: break-word;
                 user-select: none;
             }
-
         }
-
     }
 
     .linkMenu-item-content {
@@ -114,7 +107,6 @@
     }
 
     &:hover {
-
         background-color: rgb(201, 239, 252);
 
         .linkMenu-item-buttons {
@@ -122,7 +114,6 @@
         }
 
         .linkMenu-item-content {
-
             .linkMenu-destination-title {
                 text-decoration: underline;
                 color: rgb(60, 90, 156);
@@ -135,10 +126,7 @@
 
 .linkMenu-item-buttons {
     //@extend: right;
-    position: absolute;
-    top: 50%;
-    right: 0;
-    transform: translateY(-50%);
+    position: relative;
     display: none;
 
     .button {
@@ -172,4 +160,4 @@
             cursor: pointer;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 387e0e3d5..c3b5fa997 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,12 +1,12 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, observable } from 'mobx';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../../../fields/Doc';
-import { Cast, StrCast } from '../../../fields/Types';
+import { Cast, DocCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
-import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
@@ -16,6 +16,9 @@ import { DocumentView } from '../nodes/DocumentView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
 import React = require('react');
+import { SettingsManager } from '../../util/SettingsManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import { undoBatch } from '../../util/UndoManager';
 
 interface LinkMenuItemProps {
     groupType: string;
@@ -76,8 +79,19 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         this._showMore = !this._showMore;
     }
 
-    onEdit = (e: React.PointerEvent): void => {
-        LinkManager.currentLink = this.props.linkDoc;
+    @computed get sourceAnchor() {
+        const ldoc = this.props.linkDoc;
+        if (this.props.sourceDoc !== ldoc.anchor1 && this.props.sourceDoc !== ldoc.anchor2) {
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor1);
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor2);
+        }
+        return this.props.sourceDoc;
+    }
+    @action
+    onEdit = (e: React.PointerEvent) => {
+        const sel = SelectionManager.Views();
+        LinkManager.currentLink = this.props.linkDoc === LinkManager.currentLink ? undefined : this.props.linkDoc;
+        LinkManager.currentLinkAnchor = this.sourceAnchor;
         setupMoveUpEvents(
             this,
             e,
@@ -88,7 +102,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                 return true;
             },
             emptyFunction,
-            () => this.props.showEditor(this.props.linkDoc)
+            action(() => {
+                if ((SettingsManager.propertiesWidth ?? 0) < 100) {
+                    SettingsManager.propertiesWidth = 250;
+                }
+                //this.props.showEditor(this.props.linkDoc);
+            })
         );
     };
 
@@ -123,6 +142,8 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         );
     };
 
+    deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.deleteLink(this.props.linkDoc))));
+
     render() {
         const destinationIcon = Doc.toIcon(this.props.destinationDoc) as any as IconProp;
 
@@ -141,7 +162,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                 : undefined;
 
         return (
-            <div className="linkMenu-item">
+            <div className="linkMenu-item" style={{ background: LinkManager.currentLink === this.props.linkDoc ? 'lightBlue' : undefined }}>
                 <div className={'linkMenu-item-content expand-two'}>
                     <div
                         ref={this._drag}
@@ -180,7 +201,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                         <div className="linkMenu-item-buttons" ref={this._buttonRef}>
                             <Tooltip title={<div className="dash-tooltip">Edit Link</div>}>
                                 <div className="button" ref={this._editRef} onPointerDown={this.onEdit} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon="edit" size="sm" />
+                                    <FontAwesomeIcon className="fa-icon" icon="chevron-down" size="sm" />
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">Delete Link</div>}>
+                                <div className="button" style={{ background: 'red' }} ref={this._editRef} onPointerDown={this.deleteLink} onClick={e => e.stopPropagation()}>
+                                    <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" />
                                 </div>
                             </Tooltip>
                         </div>
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 135fbca31..b326865b3 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -124,6 +124,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         return undefined;
     }
     @observable _showEditor = false;
+
+    @action
     editLink = (e: React.PointerEvent): void => {
         LinkManager.currentLink = this.props.linkDoc;
         setupMoveUpEvents(
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index adfd2fda1..1cb6bc3a2 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -33,7 +33,7 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import { ScriptingBox } from '../ScriptingBox';
 import './PresBox.scss';
-import { PresEffect, PresMovement, PresStatus } from './PresEnums';
+import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
 import { map } from 'bluebird';
 import { OpenWhere, OpenWhereMod } from '../DocumentView';
 const { Howl } = require('howler');
@@ -71,10 +71,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Doc, root: Doc) {
         const effectProps = {
-            left: presEffectDoc?.presEffectDirection === PresEffect.Left,
-            right: presEffectDoc?.presEffectDirection === PresEffect.Right,
-            top: presEffectDoc?.presEffectDirection === PresEffect.Top,
-            bottom: presEffectDoc?.presEffectDirection === PresEffect.Bottom,
+            left: presEffectDoc?.presEffectDirection === PresEffectDirection.Left,
+            right: presEffectDoc?.presEffectDirection === PresEffectDirection.Right,
+            top: presEffectDoc?.presEffectDirection === PresEffectDirection.Top,
+            bottom: presEffectDoc?.presEffectDirection === PresEffectDirection.Bottom,
             opposite: true,
             delay: 0,
             duration: Cast(presEffectDoc?.presTransition, 'number', null),
@@ -1191,7 +1191,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    updateEffectDirection = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
+    updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
 
     @undoBatch
     @action
@@ -1236,12 +1236,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 {movement}
             </div>
         );
-        const presDirection = (direction: PresEffect, icon: string, gridColumn: number, gridRow: number, opts: object) => {
-            const color = this.activeItem.presEffectDirection === direction || (direction === PresEffect.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
+        const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
+            const color = this.activeItem.presEffectDirection === direction || (direction === PresEffectDirection.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
             return (
                 <Tooltip title={<div className="dash-tooltip">{direction}</div>}>
                     <div
-                        style={{ ...opts, border: direction === PresEffect.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
+                        style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
                         onClick={() => this.updateEffectDirection(direction)}>
                         {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
                     </div>
@@ -1405,11 +1405,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
                             </div>
                             <div className="effectDirection" style={{ display: effect === 'None' ? 'none' : 'grid', width: 40 }}>
-                                {presDirection(PresEffect.Left, 'angle-right', 1, 2, {})}
-                                {presDirection(PresEffect.Right, 'angle-left', 3, 2, {})}
-                                {presDirection(PresEffect.Top, 'angle-down', 2, 1, {})}
-                                {presDirection(PresEffect.Bottom, 'angle-up', 2, 3, {})}
-                                {presDirection(PresEffect.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+                                {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+                                {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+                                {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+                                {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+                                {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
                             </div>
                         </div>
                     )}
@@ -1428,7 +1428,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     applyTo = (array: Doc[]) => {
         this.updateMovement(this.activeItem.presMovement as PresMovement, true);
         this.updateEffect(this.activeItem.presEffect as PresEffect, true);
-        this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffect, true);
+        this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffectDirection, true);
         const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem;
         array.forEach(curDoc => {
             curDoc.presTransition = presTransition;
diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts
index c6a222c3a..034f7588b 100644
--- a/src/client/views/nodes/trails/PresEnums.ts
+++ b/src/client/views/nodes/trails/PresEnums.ts
@@ -14,6 +14,9 @@ export enum PresEffect {
     Bounce = 'Bounce',
     Roll = 'Roll',
     None = 'None',
+}
+
+export enum PresEffectDirection {
     Left = 'Enter from left',
     Right = 'Enter from right',
     Center = 'Enter from center',
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 75801b68c..c10751698 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1268,7 +1268,7 @@ export namespace Doc {
 
     let _lastDate = 0;
     export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presEffect?: Doc) {
-        linkFollowUnhighlight();
+        //linkFollowUnhighlight();
         // runInAction(() => presEffect && (HighlightBrush.linkFollowEffect = undefined));
         // setTimeout(() => runInAction(() => presEffect && (HighlightBrush.linkFollowEffect = presEffect)));
         runInAction(() => presEffect && (HighlightBrush.linkFollowEffect = presEffect));
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 24b5a359d..10324449f 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -93,7 +93,7 @@ export const documentSchema = createSchema({
     layers: listSpec('string'), // which layers the document is part of
     _lockedPosition: 'boolean', // whether the document can be moved (dragged)
     _lockedTransform: 'boolean', // whether a freeformview can pan/zoom
-    displayArrow: 'boolean', // toggles directed arrows
+    linkDisplayArrow: 'boolean', // toggles directed arrows
 
     // drag drop properties
     _stayInCollection: 'boolean', // whether document can be dropped into a different collection
-- 
cgit v1.2.3-70-g09d2


From 403d1c4fece9efa663e0fd7161afff9f27cf670c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 16 Dec 2022 15:07:50 -0500
Subject: fixed problem with undo.  regularized all linkfollowing anchor fields
 to start with followLink<xxx>.   added ease vs linear flag for scroll
 transitions in link and preselmeent navigations. added link follow to move
 target to specified offset from source.  shifted from setting dropAction on
 items to setting childDropAction on collections

---
 src/Utils.ts                                       |  29 +++--
 src/client/documents/Documents.ts                  |   4 +-
 src/client/util/CurrentUserUtils.ts                |  35 +++---
 src/client/util/LinkFollower.ts                    |  78 ++++++++----
 src/client/views/DocumentButtonBar.scss            |  17 +++
 src/client/views/DocumentButtonBar.tsx             |  68 +++++++---
 src/client/views/GestureOverlay.tsx                |   1 -
 src/client/views/PropertiesView.tsx                |  88 +++++++++----
 .../views/collections/CollectionNoteTakingView.tsx |   4 +-
 .../views/collections/CollectionPileView.tsx       |   2 +-
 .../views/collections/CollectionStackingView.tsx   |   4 +-
 .../views/collections/CollectionTimeView.tsx       |   2 +-
 src/client/views/collections/TabDocView.tsx        |   5 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   6 +-
 .../collectionLinear/CollectionLinearView.tsx      |   3 +-
 src/client/views/linking/LinkPopup.scss            |   5 +-
 src/client/views/linking/LinkPopup.tsx             |  13 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   2 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   2 +-
 src/client/views/nodes/DocumentView.tsx            |  11 +-
 src/client/views/nodes/LabelBox.tsx                | 140 ++++++++++++---------
 src/client/views/nodes/WebBox.tsx                  |   8 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   5 +-
 src/client/views/nodes/trails/PresBox.tsx          |  37 ++++--
 src/client/views/nodes/trails/PresEnums.ts         |   1 +
 src/client/views/pdf/AnchorMenu.tsx                |   3 +-
 src/client/views/pdf/PDFViewer.tsx                 |  21 ++--
 src/client/views/search/SearchBox.tsx              |  29 ++++-
 src/fields/util.ts                                 |  10 +-
 29 files changed, 416 insertions(+), 217 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 5e0514bc6..9d3b9eb2b 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -558,9 +558,13 @@ export namespace JSONUtils {
     }
 }
 
-const easeInOutQuad = (currentTime: number, start: number, change: number, duration: number) => {
-    let newCurrentTime = currentTime / (duration / 2);
+const easeFunc = (transition: 'ease' | 'linear' | undefined, currentTime: number, start: number, change: number, duration: number) => {
+    if (transition === 'linear') {
+        let newCurrentTime = currentTime / duration; //  currentTime / (duration / 2);
+        return start + newCurrentTime * change;
+    }
 
+    let newCurrentTime = currentTime / (duration / 2);
     if (newCurrentTime < 1) {
         return (change / 2) * newCurrentTime * newCurrentTime + start;
     }
@@ -569,23 +573,28 @@ const easeInOutQuad = (currentTime: number, start: number, change: number, durat
     return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start;
 };
 
-export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number) {
+export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number, transition: 'ease' | 'linear' | undefined, stopper?: () => void) {
+    stopper?.();
     const elements = element instanceof HTMLElement ? [element] : element;
     const starts = elements.map(element => element.scrollTop);
     const startDate = new Date().getTime();
-
+    let _stop = false;
+    const stop = () => (_stop = true);
     const animateScroll = () => {
         const currentDate = new Date().getTime();
         const currentTime = currentDate - startDate;
-        elements.map((element, i) => (element.scrollTop = easeInOutQuad(currentTime, starts[i], to - starts[i], duration)));
+        elements.map((element, i) => (element.scrollTop = easeFunc(transition, currentTime, starts[i], to - starts[i], duration)));
 
-        if (currentTime < duration) {
-            requestAnimationFrame(animateScroll);
-        } else {
-            elements.forEach(element => (element.scrollTop = to));
+        if (!_stop) {
+            if (currentTime < duration) {
+                requestAnimationFrame(animateScroll);
+            } else {
+                elements.forEach(element => (element.scrollTop = to));
+            }
         }
     };
     animateScroll();
+    return stop;
 }
 
 export function smoothScrollHorizontal(duration: number, element: HTMLElement | HTMLElement[], to: number) {
@@ -596,7 +605,7 @@ export function smoothScrollHorizontal(duration: number, element: HTMLElement |
     const animateScroll = () => {
         const currentDate = new Date().getTime();
         const currentTime = currentDate - startDate;
-        elements.map((element, i) => (element.scrollLeft = easeInOutQuad(currentTime, starts[i], to - starts[i], duration)));
+        elements.map((element, i) => (element.scrollLeft = easeFunc('ease', currentTime, starts[i], to - starts[i], duration)));
 
         if (currentTime < duration) {
             requestAnimationFrame(animateScroll);
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d13d96dd3..634b14822 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -37,7 +37,7 @@ import { FontIconBox } from '../views/nodes/button/FontIconBox';
 import { ColorBox } from '../views/nodes/ColorBox';
 import { ComparisonBox } from '../views/nodes/ComparisonBox';
 import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
-import { DocFocusOptions, OpenWhereMod } from '../views/nodes/DocumentView';
+import { DocFocusOptions, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
 import { EquationBox } from '../views/nodes/EquationBox';
 import { FieldViewProps } from '../views/nodes/FieldView';
 import { FilterBox } from '../views/nodes/FilterBox';
@@ -1322,7 +1322,7 @@ export namespace DocUtils {
         return DocUtils.ActiveRecordings.map(audio => {
             const sourceDoc = getSourceDoc();
             const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
-            link && (link.followLinkLocation = 'add:right');
+            link && (link.followLinkLocation = OpenWhere.addRight);
             return link;
         });
     }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 635980e03..5549769aa 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -22,7 +22,7 @@ import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
 import { OverlayView } from "../views/OverlayView";
-import { DragManager } from "./DragManager";
+import { DragManager, dropActionType } from "./DragManager";
 import { MakeTemplate } from "./DropConverter";
 import { LinkManager } from "./LinkManager";
 import { ScriptingGlobals } from "./ScriptingGlobals";
@@ -306,7 +306,7 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
             const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
-                _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias",
+                _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, 
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
@@ -316,7 +316,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true,
             _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-            childDocumentsActive: true
+            childDocumentsActive: true, childDropAction: 'alias' 
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -352,15 +352,15 @@ export class CurrentUserUtils {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
                 title, icon, target, btnType: ButtonType.MenuButton, system: true, dontUndo: true,  dontRegisterView: true,
-                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, _dropAction: "alias",
-                _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
+                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+                _removeDropProperties: new List<string>(["_stayInCollection"]),
             };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
         });
 
         const reqdStackOpts:DocumentOptions ={
-            title: "menuItemPanel", childDropAction: "alias", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
-            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
+            title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
     }
@@ -397,14 +397,14 @@ export class CurrentUserUtils {
     // sets up the main document for the mobile button
     static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, {
         ...opts,
-        _removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15,
+         _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15,
         borderRounding: "5px", boxShadow: "0 0", system: true
     }) as any as Doc
 
     // sets up the text container for the information contained within the mobile button
     static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, {
         ...opts,
-        _removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25,
+        _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25,
         backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, system: true
     }) as any as Doc
 
@@ -573,8 +573,8 @@ export class CurrentUserUtils {
     })
 
     static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
-        btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, 
-        _removeDropProperties: new List<string>(["_dropAction", "_hideContextMenu", "stayInCollection"]), 
+        btnType: ButtonType.ToolButton, _forceActive: true, _hideContextMenu: true, 
+        _removeDropProperties: new List<string>([ "_hideContextMenu", "stayInCollection"]), 
         _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts,
     })
 
@@ -595,12 +595,15 @@ export class CurrentUserUtils {
             // { scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, opts:{title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,  width: 20,}  },
              ];
         const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
-        const dockBtnsReqdOpts = {
-            title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard",
+        const dockBtnsReqdOpts:DocumentOptions = {
+            title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'alias',
             childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
         };
         reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
-        reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
+        reaction(() => UndoManager.undoStack.slice(), () => {
+            console.log(UndoManager.undoStack)
+            Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4;
+        }, { fireImmediately: true });
         return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
     }
 
@@ -687,7 +690,7 @@ export class CurrentUserUtils {
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, 
             _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true,
-            _dropAction: "alias", _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
+            _removeDropProperties: new List<string>([ "_stayInCollection"]),
         };
         const reqdFuncs:{[key:string]:any} = {
             ...params.funcs,
@@ -698,7 +701,7 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'alias', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 0285803e8..94badbb44 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,5 +1,6 @@
 import { action, runInAction } from 'mobx';
-import { Doc, DocListCast, Opt } from '../../fields/Doc';
+import { Doc, DocListCast, Opt, WidthSym } from '../../fields/Doc';
+import { listSpec } from '../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentDecorations } from '../views/DocumentDecorations';
@@ -98,32 +99,59 @@ export class LinkFollower {
                     : linkDoc.anchor1
             ) as Doc;
             if (target) {
-                const options: DocFocusOptions = {
-                    playAudio: BoolCast(sourceDoc.followLinkAudio),
-                    toggleTarget: BoolCast(sourceDoc.followLinkToggle),
-                    willPan: true,
-                    willPanZoom: BoolCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkZoom, false),
-                    zoomTime: NumCast(LinkManager.getOppositeAnchor(linkDoc, target)?.linkTransitionTime, 500),
-                    zoomScale: Cast(sourceDoc.linkZoomScale, 'number', null),
-                    effect: sourceDoc,
-                    originatingDoc: sourceDoc,
+                const doFollow = (canToggle?: boolean) => {
+                    const options: DocFocusOptions = {
+                        playAudio: BoolCast(sourceDoc.followLinkAudio),
+                        toggleTarget: canToggle && BoolCast(sourceDoc.followLinkToggle),
+                        willPan: true,
+                        willPanZoom: BoolCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkZoom, false),
+                        zoomTime: NumCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkTransitionTime, 500),
+                        zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null),
+                        easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any,
+                        effect: sourceDoc,
+                        originatingDoc: sourceDoc,
+                    };
+                    if (target.TourMap) {
+                        const fieldKey = Doc.LayoutFieldKey(target);
+                        const tour = DocListCast(target[fieldKey]).reverse();
+                        LightboxView.SetLightboxDoc(currentContext, undefined, tour);
+                        setTimeout(LightboxView.Next);
+                        allFinished();
+                    } else {
+                        const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
+                        const containerDoc = containerAnnoDoc || target;
+                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : ([] as Doc[]);
+                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+                        }
+                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
+                        DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.inPlace), finished), targetContexts, allFinished);
+                    }
                 };
-                if (target.TourMap) {
-                    const fieldKey = Doc.LayoutFieldKey(target);
-                    const tour = DocListCast(target[fieldKey]).reverse();
-                    LightboxView.SetLightboxDoc(currentContext, undefined, tour);
-                    setTimeout(LightboxView.Next);
-                    allFinished();
-                } else {
-                    const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
-                    const containerDoc = containerAnnoDoc || target;
-                    var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : ([] as Doc[]);
-                    while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
-                        containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+                let movedTarget = false;
+                if (sourceDoc.followLinkLocation === OpenWhere.inParent) {
+                    const sourceDocParent = DocCast(sourceDoc.context);
+                    if (target.context instanceof Doc && target.context !== sourceDocParent) {
+                        Doc.RemoveDocFromList(target.context, Doc.LayoutFieldKey(target.context), target);
+                        movedTarget = true;
+                    }
+                    if (!DocListCast(sourceDocParent[Doc.LayoutFieldKey(sourceDocParent)]).includes(target)) {
+                        Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target);
+                        movedTarget = true;
+                    }
+                    target.context = sourceDocParent;
+                    const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
+                    if (sourceDoc.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
+                        target.x = moveTo[0];
+                        movedTarget = true;
+                    }
+                    if (sourceDoc.followLinkYoffset !== undefined && moveTo[1] !== target.y) {
+                        target.y = moveTo[1];
+                        movedTarget = true;
                     }
-                    const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
-                    DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'inPlace'), finished), targetContexts, allFinished);
-                }
+                    if (movedTarget) setTimeout(doFollow);
+                    else doFollow(true);
+                } else doFollow(true);
             } else {
                 allFinished();
             }
diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss
index f9c988fdd..835d6c8bb 100644
--- a/src/client/views/DocumentButtonBar.scss
+++ b/src/client/views/DocumentButtonBar.scss
@@ -28,6 +28,15 @@ $linkGap: 3px;
     height: 20px;
     align-items: center;
 }
+.documentButtonBar-linkTypes {
+    position: absolute;
+    display: none;
+    width: 60px;
+    top: -14px;
+    background: black;
+    height: 20px;
+    align-items: center;
+}
 .documentButtonBar-followTypes {
     width: 20px;
     display: none;
@@ -55,6 +64,14 @@ $linkGap: 3px;
         }
     }
 }
+.documentButtonBar-link {
+    color: white;
+    &:hover {
+        .documentButtonBar-linkTypes {
+            display: flex;
+        }
+    }
+}
 
 .documentButtonBar-pinIcon {
     &:hover {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 5969d55e9..90c6c040c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,24 +1,24 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, observable, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { BoolCast, Cast, NumCast } from '../../fields/Types';
+import { Cast, NumCast } from '../../fields/Types';
 import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { Docs } from '../documents/Documents';
 import { DragManager } from '../util/DragManager';
 import { SelectionManager } from '../util/SelectionManager';
-import { SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { TabDocView } from './collections/TabDocView';
 import './DocumentButtonBar.scss';
 import { Colors } from './global/globalEnums';
+import { LinkPopup } from './linking/LinkPopup';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView, DocumentViewInternal, OpenWhereMod } from './nodes/DocumentView';
@@ -26,6 +26,7 @@ import { DashFieldView } from './nodes/formattedText/DashFieldView';
 import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
 import { TemplateMenu } from './TemplateMenu';
 import React = require('react');
+import { DocumentType } from '../documents/DocumentTypes';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -255,6 +256,28 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         );
     }
 
+    @observable subLink = '';
+    @computed get linkButton() {
+        const targetDoc = this.view0?.props.Document;
+        return !targetDoc || !this.view0 ? null : (
+            <div className="documentButtonBar-icon documentButtonBar-link">
+                <div className="documentButtonBar-linkTypes">
+                    <Tooltip title={<div>search for target</div>}>
+                        <div className="documentButtonBar-button">
+                            <button style={{ backgroundColor: 'transparent', width: 35, height: 35, display: 'flex', justifyContent: 'center', alignItems: 'center', position: 'relative' }} onPointerDown={this.toggleLinkSearch}>
+                                <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
+                                <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'center', top: 9, left: 2 }} icon={'link'} size="lg" />
+                            </button>
+                        </div>
+                    </Tooltip>
+                </div>
+                <div style={{ width: 25, height: 25 }}>
+                    <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
+                </div>
+            </div>
+        );
+    }
+
     @observable subPin = '';
     @computed
     get pinButton() {
@@ -284,7 +307,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                                     .views()
                                     .filter(v => v)
                                     .map(dv => dv!.rootDoc);
-                                TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout, pinDocContent, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+                                TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout, pinDocContent, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null), currentFrame: Cast(docs.lastElement()?.currentFrame, 'number', null) });
                                 e.stopPropagation();
                             }}
                         />
@@ -319,12 +342,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
         return !targetDoc ? null : (
-            <Tooltip
-                title={
-                    <>
-                        <div className="dash-tooltip">{'Open Sharing Manager'}</div>
-                    </>
-                }>
+            <Tooltip title={<div className="dash-tooltip">{'Open Sharing Manager'}</div>}>
                 <div className="documentButtonBar-icon" style={{ color: 'white' }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
                     <FontAwesomeIcon className="documentdecorations-icon" icon="users" />
                 </div>
@@ -347,12 +365,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     get metadataButton() {
         const view0 = this.view0;
         return !view0 ? null : (
-            <Tooltip
-                title={
-                    <>
-                        <div className="dash-tooltip">Show metadata panel</div>
-                    </>
-                }>
+            <Tooltip title={<div className="dash-tooltip">Show metadata panel</div>}>
                 <div className="documentButtonBar-linkFlyout">
                     <Flyout
                         anchorPoint={anchorPoints.LEFT_TOP}
@@ -464,6 +477,12 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         simulateMouseClick(child, e.clientX, e.clientY - 30, e.screenX, e.screenY - 30);
     };
 
+    @observable _showLinkPopup = false;
+    @action
+    toggleLinkSearch = (e: React.PointerEvent) => {
+        this._showLinkPopup = !this._showLinkPopup;
+        e.stopPropagation();
+    };
     render() {
         if (!this.view0) return null;
 
@@ -476,9 +495,20 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                 <div className="documentButtonBar-button">
                     <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} ShowCount={true} />
                 </div>
-                <div className="documentButtonBar-button">
-                    <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
-                </div>
+                {this._showLinkPopup ? (
+                    <div style={{ position: 'absolute', zIndex: 1000 }}>
+                        <LinkPopup
+                            key="popup"
+                            showPopup={this._showLinkPopup}
+                            linkCreated={link => (link.linkDisplay = !this.props.views().lastElement()?.rootDoc.isLinkButton)}
+                            linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.()}
+                            linkFrom={() => this.props.views().lastElement()?.rootDoc}
+                        />
+                    </div>
+                ) : (
+                    <div className="documentButtonBar-button">{this.linkButton}</div>
+                )}
+
                 {(DocumentLinksButton.StartLink || Doc.UserDoc()['documentLinksButton-fullMenu']) && DocumentLinksButton.StartLink !== doc ? (
                     <div className="documentButtonBar-button">
                         <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index a29073f14..e3328fb4c 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -122,7 +122,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
                 onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
                 backgroundColor: data.backgroundColor,
-                _removeDropProperties: new List<string>(['dropAction']),
                 dragFactory: data.dragFactory,
                 system: true,
             })
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index e43b160b8..92c5708aa 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1414,7 +1414,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     changeFollowBehavior = action((follow: string) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = follow));
 
     @undoBatch
-    changeAnimationBehavior = action((behavior: string) => this.sourceAnchor && (this.sourceAnchor.linkAnimEffect = behavior));
+    changeAnimationBehavior = action((behavior: string) => this.sourceAnchor && (this.sourceAnchor.followLinkAnimEffect = behavior));
 
     @undoBatch
     changeEffectDirection = action((effect: PresEffectDirection) => this.sourceAnchor && (this.sourceAnchor.linkAnimDirection = effect));
@@ -1474,8 +1474,20 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return selAnchor ?? (LinkManager.currentLink && this.destinationAnchor ? LinkManager.getOppositeAnchor(LinkManager.currentLink, this.destinationAnchor) : LinkManager.currentLink);
     }
 
-    toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc) => {
-        anchor && setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (anchor[prop] = !anchor[prop]))));
+    toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: any = true, ovalue: any = false, cb: (val: any) => any = val => val) => {
+        anchor &&
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                emptyFunction,
+                undoBatch(
+                    action(() => {
+                        anchor[prop] = anchor[prop] === value ? ovalue : value;
+                        this.selectedDoc && cb(anchor[prop]);
+                    })
+                )
+            );
     };
 
     @computed
@@ -1516,7 +1528,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (change) scale += change;
         if (scale < 0.01) scale = 0.01;
         if (scale > 1) scale = 1;
-        this.sourceAnchor && (this.sourceAnchor.linkZoomScale = scale);
+        this.sourceAnchor && (this.sourceAnchor.followLinkZoomScale = scale);
     };
 
     /**
@@ -1532,7 +1544,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     render() {
         const isNovice = Doc.noviceMode;
-        const zoom = Number((NumCast(this.sourceAnchor?.linkZoomScale, 1) * 100).toPrecision(3));
+        const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3));
         const targZoom = this.sourceAnchor?.followLinkZoom;
         const indent = 30;
         const hasSelectedAnchor = SelectionManager.Views().some(dv => DocListCast(this.sourceAnchor?.links).includes(LinkManager.currentLink!));
@@ -1610,21 +1622,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                             <p>Follow by</p>
                                             <select onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkLocation, 'default')}>
                                                 <option value="default">Default</option>
-                                                <option value="add:left">Opening in new left pane</option>
-                                                <option value="add:right">Opening in new right pane</option>
-                                                <option value="replace:left">Replacing left tab</option>
-                                                <option value="replace:right">Replacing right tab</option>
-                                                <option value="fullScreen">Overlaying current tab</option>
-                                                <option value="lightbox">Opening in lightbox</option>
-                                                <option value="add">Opening in new tab</option>
-                                                <option value="replace">Replacing current tab</option>
-                                                <option value="inPlace">Opening in place</option>
+                                                <option value={OpenWhere.addLeft}>Opening in new left pane</option>
+                                                <option value={OpenWhere.addRight}>Opening in new right pane</option>
+                                                <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
+                                                <option value={OpenWhere.replaceRight}>Replacing right tab</option>
+                                                <option value={OpenWhere.fullScreen}>Overlaying current tab</option>
+                                                <option value={OpenWhere.lightbox}>Opening in lightbox</option>
+                                                <option value={OpenWhere.add}>Opening in new tab</option>
+                                                <option value={OpenWhere.replace}>Replacing current tab</option>
+                                                <option value={OpenWhere.inParent}>Opening in same collection</option>
+                                                <option value={OpenWhere.inPlace}>Opening in place</option>
                                                 {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
                                             </select>
                                         </div>
                                         <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
                                             <p>Animation</p>
-                                            <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.linkAnimEffect, 'default')}>
+                                            <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
                                                 <option value="default">Default</option>
                                                 {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
                                                     <option value={effect.toString()}>{effect.toString()}</option>
@@ -1642,9 +1655,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                             '0.1',
                                             '0.1',
                                             '10',
-                                            NumCast(this.sourceAnchor?.linkTransitionTime) / 1000,
+                                            NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
                                             true,
-                                            (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.linkTransitionTime = timeInMS)),
+                                            (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
                                             indent
                                         )}{' '}
                                         <div
@@ -1681,9 +1694,42 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                                 <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                             </button>
                                         </div>
+                                        <div className="propertiesView-input inline">
+                                            <p>Ease Transitions</p>
+                                            <button
+                                                style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
+                                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
+                                                onClick={e => e.stopPropagation()}
+                                                className="propertiesButton">
+                                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                            </button>
+                                        </div>
+                                        <div className="propertiesView-input inline">
+                                            <p>Capture Offset to Target</p>
+                                            <button
+                                                style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
+                                                onPointerDown={e => {
+                                                    this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
+                                                    this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
+                                                }}
+                                                onClick={e => e.stopPropagation()}
+                                                className="propertiesButton">
+                                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                            </button>
+                                        </div>
+                                        <div className="propertiesView-input inline">
+                                            <p>Center Target (no zoom)</p>
+                                            <button
+                                                style={{ background: this.sourceAnchor?.followLinkZoomScale !== 0 ? '' : '#4476f7', borderRadius: 3 }}
+                                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomScale', this.sourceAnchor, 0, 0.5, val => !val && this.sourceAnchor && (this.sourceAnchor.followLinkZoom = true))}
+                                                onClick={e => e.stopPropagation()}
+                                                className="propertiesButton">
+                                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                            </button>
+                                        </div>
                                         <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
                                             <p>Zoom %</p>
-                                            <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
+                                            <div className="ribbon-property" style={{ display: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? 'none' : 'inline-flex' }}>
                                                 <input className="presBox-input" style={{ width: '100%' }} type="number" value={zoom} />
                                                 <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
                                                     <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
@@ -1695,18 +1741,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                                 </div>
                                             </div>
                                             <button
-                                                style={{ background: !targZoom ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
+                                                style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
                                                 onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
                                                 onClick={e => e.stopPropagation()}
                                                 className="propertiesButton">
                                                 <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
                                             </button>
                                         </div>
-                                        {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
+                                        {!targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
                                         <div
                                             className={'slider-headers'}
                                             style={{
-                                                display: !targZoom ? 'none' : 'grid',
+                                                display: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? 'none' : 'grid',
                                                 justifyContent: 'space-between',
                                                 width: `calc(100% - ${indent * 2}px)`,
                                                 marginLeft: indent,
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 5e389e17e..26c73438c 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -181,7 +181,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     }
 
     scrollToBottom = () => {
-        smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight);
+        smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight, 'ease');
     };
 
     // let's dive in and get the actual document we want to drag/move around
@@ -193,7 +193,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             const top = found.getBoundingClientRect().top;
             const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
             if (Math.floor(localTop[1]) !== 0) {
-                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
             }
         }
         const endFocus = async (moved: boolean) => (options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index e95622630..ba90ed8cd 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -120,7 +120,7 @@ export class CollectionPileView extends CollectionSubView() {
                         const doc = this.childDocs[0];
                         doc.x = e.clientX;
                         doc.y = e.clientY;
-                        this.props.addDocTab(doc, OpenWhere.inParent) && (this.props.removeDocument?.(doc) || false);
+                        this.props.addDocTab(doc, OpenWhere.inParentFromScreen) && (this.props.removeDocument?.(doc) || false);
                         dist = 0;
                     }
                 }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 08aebc62d..acf59b5da 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -242,7 +242,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     }
 
     scrollToBottom = () => {
-        smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight);
+        smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight, 'ease');
     };
 
     // let's dive in and get the actual document we want to drag/move around
@@ -255,7 +255,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const top = found.getBoundingClientRect().top;
             const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
             if (Math.floor(localTop[1]) !== 0) {
-                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+                smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
             }
         }
         const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index ac896a8fd..a1466bcd0 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -57,7 +57,7 @@ export class CollectionTimeView extends CollectionSubView() {
     async componentDidMount() {
         this.props.setContentView?.(this);
         //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), "");
-        ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
+        ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; useRightSplit(alias, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index a61ae680b..e45e2a1cb 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -281,6 +281,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.title = doc.title + ' (move)';
                 pinDoc.presMovement = PresMovement.Pan;
             }
+            if (pinProps?.currentFrame !== undefined) {
+                pinDoc.presCurrentFrame = pinProps?.currentFrame;
+                pinDoc.title = doc.title + ' (move)';
+                pinDoc.presMovement = PresMovement.Pan;
+            }
             if (pinDoc.isInkMask) {
                 pinDoc.presHideAfter = true;
                 pinDoc.presHideBefore = true;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4818094de..6b9eb7916 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1186,7 +1186,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const focusSpeed = options?.instant ? 0 : didMove ? options.zoomTime ?? 500 : 0;
             // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
             if (didMove) {
-                scale && (this.Document[this.scaleFieldKey] = scale);
+                options.zoomScale && scale && (this.Document[this.scaleFieldKey] = scale);
                 this.setPan(panX, panY, focusSpeed, 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
             }
 
@@ -1343,6 +1343,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     addDocTab = action((doc: Doc, where: OpenWhere) => {
         switch (where) {
             case OpenWhere.inParent:
+                return this.props.addDocument?.(doc) || false;
+            case OpenWhere.inParentFromScreen:
                 return (
                     this.props.addDocument?.(
                         (doc instanceof Doc ? [doc] : doc).map(doc => {
@@ -1741,7 +1743,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             doc.x = scr?.[0];
             doc.y = scr?.[1];
         });
-        this.props.addDocTab(childDocs as any as Doc, OpenWhere.inParent);
+        this.props.addDocTab(childDocs as any as Doc, OpenWhere.inParentFromScreen);
         this.props.ContainingCollectionView?.removeDocument(this.props.Document);
     };
 
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index ff263c4f3..a2330c6b2 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -9,7 +9,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields
 import { emptyFunction, returnEmptyDoclist, returnTrue, StopEvent, Utils } from '../../../../Utils';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager } from '../../../util/DragManager';
+import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { Colors, Shadows } from '../../global/globalEnums';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
@@ -190,6 +190,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     moveDocument={this.props.moveDocument}
                     addDocTab={this.props.addDocTab}
                     pinToPres={emptyFunction}
+                    dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
                     rootSelected={this.props.isSelected}
                     removeDocument={this.props.removeDocument}
                     ScreenToLocalTransform={docXf}
diff --git a/src/client/views/linking/LinkPopup.scss b/src/client/views/linking/LinkPopup.scss
index 60c9ebfcd..b20ad9476 100644
--- a/src/client/views/linking/LinkPopup.scss
+++ b/src/client/views/linking/LinkPopup.scss
@@ -1,7 +1,7 @@
 .linkPopup-container {
     background: white;
     box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
-    top: 35px;
+    top: 0;
     height: 200px;
     width: 200px;
     position: absolute;
@@ -38,8 +38,7 @@
         }
     }
 
-
     .searchBox-container {
         background: pink;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 0bcb68f82..7bdace2b6 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -11,10 +11,13 @@ import { SearchBox } from '../search/SearchBox';
 import { DefaultStyleProvider } from '../StyleProvider';
 import './LinkPopup.scss';
 import React = require('react');
+import { OpenWhere } from '../nodes/DocumentView';
 
 interface LinkPopupProps {
     showPopup: boolean;
     linkFrom?: () => Doc | undefined;
+    linkCreateAnchor?: () => Doc | undefined;
+    linkCreated?: (link: Doc) => void;
     // groupType: string;
     // linkDoc: Doc;
     // docView: DocumentView;
@@ -32,14 +35,10 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
 
     // TODO: should check for valid URL
     @undoBatch
-    makeLinkToURL = (target: string, lcoation: string) => {
-        ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, 'onRadd:rightight', target, target);
-    };
+    makeLinkToURL = (target: string, lcoation: string) => ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, OpenWhere.addRight, target, target);
 
     @action
-    onLinkChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.linkURL = e.target.value;
-    };
+    onLinkChange = (e: React.ChangeEvent<HTMLInputElement>) => (this.linkURL = e.target.value);
 
     getPWidth = () => 500;
     getPHeight = () => 500;
@@ -67,7 +66,9 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         Document={Doc.MySearcher}
                         DataDoc={Doc.MySearcher}
                         linkFrom={linkDoc}
+                        linkCreateAnchor={this.props.linkCreateAnchor}
                         linkSearch={true}
+                        linkCreated={this.props.linkCreated}
                         fieldKey="data"
                         dropAction="move"
                         isSelected={returnTrue}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index bf1f13a06..f8ef87fb1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -184,7 +184,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 topDoc.x = spt[0];
                 topDoc.y = spt[1];
                 this.props.removeDocument?.(topDoc);
-                this.props.addDocTab(topDoc, OpenWhere.inParent);
+                this.props.addDocTab(topDoc, OpenWhere.inParentFromScreen);
             } else {
                 const spt = this.screenToLocalTransform().inverse().transformPoint(0, 0);
                 const fpt = screenXf.transformPoint(spt[0], spt[1]);
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 99fa62fa7..6f3152981 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -305,7 +305,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                     top: 0,
                     pointerEvents: 'none',
                 }}>
-                {!DocumentLinksButton.LinkEditorDocView ? this.linkButtonInner : <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>}
+                <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>
             </div>
         );
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c9fbe7a98..76cc6800a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -75,6 +75,7 @@ export enum OpenWhere {
     inPlace = 'inPlace',
     lightbox = 'lightbox',
     add = 'add',
+    addLeft = 'add:left',
     addRight = 'add:right',
     addBottom = 'add:bottom',
     dashboard = 'dashboard',
@@ -82,7 +83,10 @@ export enum OpenWhere {
     fullScreen = 'fullScreen',
     toggle = 'toggle',
     replace = 'replace',
+    replaceRight = 'replace:right',
+    replaceLeft = 'replace:left',
     inParent = 'inParent',
+    inParentFromScreen = 'inParentFromScreen',
 }
 export enum OpenWhereMod {
     none = '',
@@ -108,6 +112,7 @@ export interface DocFocusOptions {
     playAudio?: boolean; // whether to play audio annotation on focus
     toggleTarget?: boolean; // whether to toggle target on and off
     originatingDoc?: Doc; // document that triggered the focus
+    easeFunc?: 'linear' | 'ease'; // transition method for scrolling
 }
 export type DocAfterFocusFunc = (notFocused: boolean) => Promise<ViewAdjustment>;
 export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void;
@@ -737,7 +742,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     document.removeEventListener('pointerup', this.onPointerUp);
                     this._cursorTimer && clearTimeout(this._cursorTimer);
                     this._cursorPress = false;
-                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.props.dropAction || this.Document.dropAction || undefined) as dropActionType));
+                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
                 }
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -854,7 +859,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' });
             DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, 'portal to:portal from');
         }
-        this.Document.followLinkLocation = 'inPlace';
+        this.Document.followLinkLocation = OpenWhere.inPlace;
         this.Document.followLinkZoom = true;
         this.Document._isLinkButton = true;
     };
@@ -1446,7 +1451,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             duration: Cast(presEffectDoc?.presTransition, 'number', null),
         };
         //prettier-ignore
-        switch (StrCast(presEffectDoc?.presEffect, StrCast(presEffectDoc?.linkAnimEffect))) {
+        switch (StrCast(presEffectDoc?.presEffect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
             default: 
             case PresEffect.None:       return renderDoc;
             case PresEffect.Zoom:       return <Zoom {...effectProps}>{renderDoc}</Zoom>;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index b58a9affb..10897b48f 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -15,16 +15,17 @@ import { FieldView, FieldViewProps } from './FieldView';
 import BigText from './LabelBigText';
 import './LabelBox.scss';
 
-
 export interface LabelBoxProps {
     label?: string;
 }
 
 @observer
-export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps)>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); }
+export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(LabelBox, fieldKey);
+    }
     public static LayoutStringWithTitle(fieldStr: string, label?: string) {
-        return !label ? LabelBox.LayoutString(fieldStr) : `<LabelBox fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`;  //e.g., "<ImageBox {...props} fieldKey={"data} />"
+        return !label ? LabelBox.LayoutString(fieldStr) : `<LabelBox fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
     }
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: any;
@@ -35,11 +36,12 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
         this._timeout && clearTimeout(this._timeout);
     }
 
+    getAnchor = () => {
+        return this.rootDoc;
+    };
+
     getTitle() {
-        return this.rootDoc["title-custom"] ? StrCast(this.rootDoc.title) :
-            this.props.label ? this.props.label :
-                typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) :
-                    StrCast(this.rootDoc.title);
+        return this.rootDoc['title-custom'] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
     }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -47,36 +49,42 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
         if (ele) {
             this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document);
         }
-    }
+    };
 
-    get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; }
+    get paramsDoc() {
+        return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc;
+    }
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        !Doc.noviceMode && funcs.push({
-            description: "Clear Script Params", event: () => {
-                const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
-                params?.map(p => this.paramsDoc[p] = undefined);
-            }, icon: "trash"
-        });
+        !Doc.noviceMode &&
+            funcs.push({
+                description: 'Clear Script Params',
+                event: () => {
+                    const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
+                    params?.map(p => (this.paramsDoc[p] = undefined));
+                },
+                icon: 'trash',
+            });
 
-        funcs.length && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: funcs, icon: "mouse-pointer" });
-    }
+        funcs.length && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: funcs, icon: 'mouse-pointer' });
+    };
 
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         const docDragData = de.complete.docDragData;
-        const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
+        const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
         const missingParams = params?.filter(p => !this.paramsDoc[p]);
         if (docDragData && missingParams?.includes((e.target as any).textContent)) {
-            this.paramsDoc[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
-                d.onDragStart ? docDragData.draggedDocuments[i] : d));
+            this.paramsDoc[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) => (d.onDragStart ? docDragData.draggedDocuments[i] : d)));
             e.stopPropagation();
         }
-    }
+    };
 
     @observable _mouseOver = false;
-    @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : "unset"; }
+    @computed get hoverColor() {
+        return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : 'unset';
+    }
 
     fitTextToBox = (r: any): any => {
         const singleLine = BoolCast(this.rootDoc._singleLine, true);
@@ -85,63 +93,73 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
             fontSizeFactor: 1,
             minimumFontSize: NumCast(this.rootDoc._minFontSize, 8),
             maximumFontSize: NumCast(this.rootDoc._maxFontSize, 1000),
-            limitingDimension: "both",
-            horizontalAlign: "center",
-            verticalAlign: "center",
-            textAlign: "center",
+            limitingDimension: 'both',
+            horizontalAlign: 'center',
+            verticalAlign: 'center',
+            textAlign: 'center',
             singleLine,
-            whiteSpace: singleLine ? "nowrap" : "pre-wrap"
+            whiteSpace: singleLine ? 'nowrap' : 'pre-wrap',
         };
         this._timeout = undefined;
         if (!r) return params;
-        if (!r.offsetHeight || !r.offsetWidth) return this._timeout = setTimeout(() => this.fitTextToBox(r));
+        if (!r.offsetHeight || !r.offsetWidth) return (this._timeout = setTimeout(() => this.fitTextToBox(r)));
         const parent = r.parentNode;
         const parentStyle = parent.style;
-        parentStyle.display = "";
-        parentStyle.alignItems = "";
-        r.setAttribute("style", "");
-        r.style.width = singleLine ? "" : "100%";
+        parentStyle.display = '';
+        parentStyle.alignItems = '';
+        r.setAttribute('style', '');
+        r.style.width = singleLine ? '' : '100%';
 
-        r.style.textOverflow = "ellipsis";
-        r.style.overflow = "hidden";
+        r.style.textOverflow = 'ellipsis';
+        r.style.overflow = 'hidden';
         BigText(r, params);
         return params;
-    }
+    };
     // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
     render() {
-        const boxParams = this.fitTextToBox(null);// this causes mobx to trigger re-render when data changes
-        const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
+        const boxParams = this.fitTextToBox(null); // this causes mobx to trigger re-render when data changes
+        const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []);
         const missingParams = params?.filter(p => !this.paramsDoc[p]);
         params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ...
         const label = this.getTitle();
         return (
-            <div className="labelBox-outerDiv"
-                onMouseLeave={action(() => this._mouseOver = false)}
-                onMouseOver={action(() => this._mouseOver = true)}
-                ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
+            <div
+                className="labelBox-outerDiv"
+                onMouseLeave={action(() => (this._mouseOver = false))}
+                onMouseOver={action(() => (this._mouseOver = true))}
+                ref={this.createDropTarget}
+                onContextMenu={this.specificContextMenu}
                 style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
-                <div className="labelBox-mainButton" style={{
-                    backgroundColor: this.hoverColor,
-                    fontSize: StrCast(this.layoutDoc._fontSize),
-                    fontFamily: StrCast(this.layoutDoc._fontFamily) || "inherit",
-                    letterSpacing: StrCast(this.layoutDoc.letterSpacing),
-                    textTransform: StrCast(this.layoutDoc.textTransform) as any,
-                    paddingLeft: NumCast(this.rootDoc._xPadding),
-                    paddingRight: NumCast(this.rootDoc._xPadding),
-                    paddingTop: NumCast(this.rootDoc._yPadding),
-                    paddingBottom: NumCast(this.rootDoc._yPadding),
-                    width: this.props.PanelWidth(),
-                    height: this.props.PanelHeight(),
-                    whiteSpace: boxParams.singleLine ? "pre" : "pre-wrap"
-                }} >
-                    <span style={{ width: boxParams.singleLine ? "" : "100%" }} ref={action((r: any) => this.fitTextToBox(r))}>
-                        {label.startsWith("#") ? (null) : label.replace(/([^a-zA-Z])/g, "$1\u200b")}
+                <div
+                    className="labelBox-mainButton"
+                    style={{
+                        backgroundColor: this.hoverColor,
+                        fontSize: StrCast(this.layoutDoc._fontSize),
+                        fontFamily: StrCast(this.layoutDoc._fontFamily) || 'inherit',
+                        letterSpacing: StrCast(this.layoutDoc.letterSpacing),
+                        textTransform: StrCast(this.layoutDoc.textTransform) as any,
+                        paddingLeft: NumCast(this.rootDoc._xPadding),
+                        paddingRight: NumCast(this.rootDoc._xPadding),
+                        paddingTop: NumCast(this.rootDoc._yPadding),
+                        paddingBottom: NumCast(this.rootDoc._yPadding),
+                        width: this.props.PanelWidth(),
+                        height: this.props.PanelHeight(),
+                        whiteSpace: boxParams.singleLine ? 'pre' : 'pre-wrap',
+                    }}>
+                    <span style={{ width: boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
+                        {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')}
                     </span>
                 </div>
-                <div className="labelBox-fieldKeyParams" >
-                    {!missingParams?.length ? (null) : missingParams.map(m => <div key={m} className="labelBox-missingParam">{m}</div>)}
+                <div className="labelBox-fieldKeyParams">
+                    {!missingParams?.length
+                        ? null
+                        : missingParams.map(m => (
+                              <div key={m} className="labelBox-missingParam">
+                                  {m}
+                              </div>
+                          ))}
                 </div>
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 64b186489..a3e83f047 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -241,7 +241,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
                 const durationSecStr = viewTrans.match(/([0-9.]*)s/);
                 const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
-                this.goTo(scrollTop, duration);
+                this.goTo(scrollTop, duration, 'ease');
             },
             { fireImmediately: true }
         );
@@ -295,7 +295,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
             if (scrollTo !== undefined && this._initialScroll === undefined) {
                 const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
-                this.goTo(scrollTo, focusSpeed);
+                this.goTo(scrollTo, focusSpeed, options.easeFunc);
                 return focusSpeed;
             } else if (!this._webPageHasBeenRendered || !this.getScrollHeight() || this._initialScroll !== undefined) {
                 this._initialScroll = scrollTo;
@@ -497,11 +497,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     };
 
-    goTo = (scrollTop: number, duration: number) => {
+    goTo = (scrollTop: number, duration: number, easeFunc: 'linear' | 'ease' | undefined) => {
         if (this._outerRef.current) {
             const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
             if (duration) {
-                smoothScroll(duration, [this._outerRef.current], scrollTop);
+                smoothScroll(duration, [this._outerRef.current], scrollTop, easeFunc);
                 this.setDashScrollTop(scrollTop, duration);
             } else {
                 this.setDashScrollTop(scrollTop);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9e91f6c46..b895043de 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1120,7 +1120,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     const durationSecStr = viewTrans.match(/([0-9.]*)s/);
                     const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
                     if (duration) {
-                        smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0));
+                        this._scrollStopper = smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0), 'ease', this._scrollStopper);
                     } else {
                         this._scrollRef.current.scrollTo({ top: pos });
                     }
@@ -1284,6 +1284,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         });
     }
     _didScroll = false;
+    _scrollStopper: undefined | (() => void);
     setupEditor(config: any, fieldKey: string) {
         const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]);
         const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
@@ -1302,7 +1303,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE);
                         const scrollPos = scrollRef.scrollTop + shift * self.props.ScreenToLocalTransform().Scale;
                         if (this._focusSpeed !== undefined) {
-                            scrollPos && smoothScroll(this._focusSpeed, scrollRef, scrollPos);
+                            scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed, scrollRef, scrollPos, 'ease', this._scrollStopper));
                         } else {
                             scrollRef.scrollTo({ top: scrollPos });
                         }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index e0f64e7aa..39328ae4a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -40,6 +40,7 @@ const { Howl } = require('howler');
 export interface PinProps {
     audioRange?: boolean;
     activeFrame?: number;
+    currentFrame?: number;
     hidePresBox?: boolean;
     pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected)
     pinDocLayout?: boolean; // pin layout info (width/height/x/y)
@@ -276,14 +277,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.rootDoc._itemIndex = index;
             const activeItem: Doc = this.activeItem;
             const targetDoc: Doc = this.targetDoc;
-            if (activeItem.presActiveFrame !== undefined) {
+            const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
+            if (activeFrame !== undefined) {
                 const transTime = NumCast(activeItem.presTransition, 500);
-                const context = DocCast(DocCast(activeItem.presentationTargetDoc).context);
+                const context = activeItem.presActiveFrame ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc);
                 if (context) {
                     const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.ComponentView as CollectionFreeFormView;
                     if (ffview) {
                         this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, ffview.childDocs.slice(), transTime);
-                        context._currentFrame = NumCast(activeItem.presActiveFrame);
+                        context._currentFrame = NumCast(activeFrame);
                     }
                 }
             }
@@ -547,11 +549,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 LightboxView.SetLightboxDoc(undefined);
                 const options: DocFocusOptions = {
                     willPan: activeItem.presMovement !== PresMovement.None,
-                    willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump,
-                    zoomScale: NumCast(activeItem.presZoom, 1),
+                    willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
+                    zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
                     zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
                     noSelect: true,
                     originatingDoc: activeItem,
+                    easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
                 };
 
                 var containerDocContext = srcContext ? [srcContext] : [];
@@ -747,8 +750,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     });
 
     movementName = action((activeItem: Doc) => {
-        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
-            activeItem.presMovement = PresMovement.Zoom;
+        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
+            return PresMovement.Zoom;
         }
         return StrCast(activeItem.presMovement);
     });
@@ -890,7 +893,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         else this.regularSelect(doc, ref, drag, focus);
     };
 
-    static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance.keyEvents(e);
+    static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e);
 
     // Key for when the presentaiton is active
     @action
@@ -1097,7 +1100,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         let timeInMS = Number(number) * 1000;
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
-        if (timeInMS > 10000) timeInMS = 10000;
+        if (timeInMS > 100000) timeInMS = 100000;
         setter(timeInMS);
     };
     setTransitionTime = (number: String, change?: number) => {
@@ -1155,6 +1158,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         activeItem.openDocument = !activeItem.openDocument;
         this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument));
     };
+    @undoBatch
+    @action
+    updateEaseFunc = (activeItem: Doc) => {
+        activeItem.presEaseFunc = activeItem.presEaseFunc === 'linear' ? 'ease' : 'linear';
+        this.selectedArray.forEach(doc => (doc.presEaseFunc = activeItem.presEaseFunc));
+    };
 
     @undoBatch
     @action
@@ -1223,7 +1232,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2;
             if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
             const effect = activeItem.presEffect ? activeItem.presEffect : PresMovement.None;
-            activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : PresMovement.Zoom;
+            // activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : PresMovement.Zoom;
             return (
                 <div
                     className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
@@ -1247,6 +1256,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                             <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}>
                                 {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.None)}
+                                {presMovement(PresMovement.Center)}
                                 {presMovement(PresMovement.Zoom)}
                                 {presMovement(PresMovement.Pan)}
                                 {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.Jump)}
@@ -1281,7 +1291,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </div>
                             </div>
                         </div>
-                        {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.setTransitionTime)}
+                        {PresBox.inputter('0.1', '0.1', '100', transitionSpeed, true, this.setTransitionTime)}
                         <div className={'slider-headers'}>
                             <div className="slider-text">Fast</div>
                             <div className="slider-text">Medium</div>
@@ -1317,6 +1327,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     Lightbox
                                 </div>
                             </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">{'Transition movement style'}</div>}>
+                                <div className="ribbon-toggle" onClick={() => this.updateEaseFunc(activeItem)}>
+                                    {`${StrCast(activeItem.presEaseFunc, 'ease')}`}
+                                </div>
+                            </Tooltip>
                         </div>
                         {type === DocumentType.AUDIO || type === DocumentType.VID ? null : (
                             <>
diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts
index 034f7588b..8c8b83fbf 100644
--- a/src/client/views/nodes/trails/PresEnums.ts
+++ b/src/client/views/nodes/trails/PresEnums.ts
@@ -1,6 +1,7 @@
 export enum PresMovement {
     Zoom = 'zoom',
     Pan = 'pan',
+    Center = 'center',
     Jump = 'jump',
     None = 'none',
 }
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index ee2ae10a7..265328036 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -210,14 +210,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                               </button>
                           </Tooltip>
                       ),
-                      //NOTE: link popup is currently in progress
                       <Tooltip key="link" title={<div className="dash-tooltip">{'Find document to link to selected text'}</div>}>
                           <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup} style={{}}>
                               <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
                               <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'top left', top: 12, left: 12 }} icon={'link'} size="lg" />
                           </button>
                       </Tooltip>,
-                      <LinkPopup key="popup" showPopup={this._showLinkPopup} linkFrom={this.onMakeAnchor} />,
+                      <LinkPopup key="popup" showPopup={this._showLinkPopup} linkCreateAnchor={this.onMakeAnchor} />,
                       AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? (
                           <></>
                       ) : (
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0703ca9b4..906dff377 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -76,7 +76,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _lastSearch = false;
     private _viewerIsSetup = false;
     private _ignoreScroll = false;
-    private _initialScroll: Opt<number>;
+    private _initialScroll: { loc: Opt<number>; easeFunc: 'linear' | 'ease' | undefined } | undefined;
     private _forcedScroll = true;
 
     selectionText = () => this._selectionText;
@@ -164,6 +164,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
     };
 
+    _scrollStopper: undefined | (() => void);
+
     // scrolls to focus on a nested annotation document.  if this is part a link preview then it will jump to the scroll location,
     // otherwise it will scroll smoothly.
     scrollFocus = (doc: Doc, scrollTop: number, options: DocFocusOptions) => {
@@ -173,12 +175,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
             const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight));
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
-                if (!this._pdfViewer) this._initialScroll = scrollTo;
-                else if (!options.instant) smoothScroll((focusSpeed = options.zoomTime??500), mainCont, scrollTo);
+                if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc };
+                else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper);
                 else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) });
             }
         } else {
-            this._initialScroll = NumCast(this.props.layoutDoc._scrollTop);
+            this._initialScroll = { loc: NumCast(this.props.layoutDoc._scrollTop), easeFunc: options.easeFunc };
         }
         return focusSpeed;
     };
@@ -202,7 +204,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this.gotoPage(NumCast(this.props.Document._curPage, 1));
         }
         document.removeEventListener('pagesinit', this.pagesinit);
-        var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : '';
+        var quickScroll: { loc?: string; easeFunc?: 'ease' | 'linear' } | undefined = { loc: this._initialScroll ? this._initialScroll.loc?.toString() : '', easeFunc: this._initialScroll ? this._initialScroll.easeFunc : undefined };
         this._disposers.scale = reaction(
             () => NumCast(this.props.layoutDoc._viewScale, 1),
             scale => (this._pdfViewer.currentScaleValue = scale),
@@ -213,7 +215,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             pos => {
                 if (!this._ignoreScroll) {
                     this._showWaiting && this.setupPdfJsViewer();
-                    const viewTrans = quickScroll ?? StrCast(this.props.Document._viewTransition);
+                    const viewTrans = quickScroll?.loc ?? StrCast(this.props.Document._viewTransition);
                     const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
                     const durationSecStr = viewTrans.match(/([0-9.]*)s/);
                     const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
@@ -221,7 +223,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     if (duration) {
                         setTimeout(
                             () => {
-                                this._mainCont.current && smoothScroll(duration, this._mainCont.current, pos);
+                                this._mainCont.current && (this._scrollStopper = smoothScroll(duration, this._mainCont.current, pos, this._initialScroll?.easeFunc ?? 'ease', this._scrollStopper));
                                 setTimeout(() => (this._forcedScroll = false), duration);
                             },
                             this._mainCont.current ? 0 : 250
@@ -236,7 +238,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         );
         quickScroll = undefined;
         if (this._initialScroll !== undefined && this._mainCont.current) {
-            this._mainCont.current?.scrollTo({ top: Math.abs(this._initialScroll || 0) });
+            this._mainCont.current?.scrollTo({ top: Math.abs(this._initialScroll?.loc || 0) });
             this._initialScroll = undefined;
         }
     };
@@ -295,7 +297,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @action
     scrollToAnnotation = (scrollToAnnotation: Doc) => {
         if (scrollToAnnotation) {
-            this.scrollFocus(scrollToAnnotation, NumCast(scrollToAnnotation.y), {zoomTime: 500});
+            this.scrollFocus(scrollToAnnotation, NumCast(scrollToAnnotation.y), { zoomTime: 500 });
             Doc.linkFollowHighlight(scrollToAnnotation);
         }
     };
@@ -452,6 +454,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     };
 
     onClick = (e: React.MouseEvent) => {
+        this._scrollStopper?.();
         if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             this._setPreviewCursor(e.clientX, e.clientY, false, false);
         }
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index b78c8654f..aac488559 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -4,10 +4,12 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { StrCast } from '../../../fields/Types';
+import { DocCast, StrCast } from '../../../fields/Types';
+import { StopEvent } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
+import { LinkManager } from '../../util/LinkManager';
 import { CollectionDockingView } from '../collections/CollectionDockingView';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
@@ -20,6 +22,8 @@ const ERROR = 0.03;
 export interface SearchBoxProps extends FieldViewProps {
     linkSearch: boolean;
     linkFrom?: (() => Doc | undefined) | undefined;
+    linkCreateAnchor?: () => Doc | undefined;
+    linkCreated?: (link: Doc) => void;
 }
 
 /**
@@ -111,10 +115,11 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
     // TODO: nda -- Change this method to change what happens when you click on the item.
     makeLink = action((linkTo: Doc) => {
-        if (this.props.linkFrom) {
-            const linkFrom = this.props.linkFrom();
+        if (this.props.linkCreateAnchor) {
+            const linkFrom = this.props.linkCreateAnchor();
             if (linkFrom) {
-                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
+                const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
+                link && this.props.linkCreated?.(link);
             }
         }
     });
@@ -380,7 +385,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
-        Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true));
+        if (!this.props.linkSearch) Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true));
         this._results.clear();
 
         if (query) {
@@ -437,6 +442,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const resultsJSX = Array();
 
+        const fromDoc = this.props.linkFrom?.();
+
         sortedResults.forEach(result => {
             var className = 'searchBox-results-scroll-view-result';
 
@@ -460,6 +467,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                                           e.stopPropagation();
                                       }
                             }
+                            style={{
+                                fontWeight: DocListCast(fromDoc?.links).find(
+                                    link => Doc.AreProtosEqual(LinkManager.getOppositeAnchor(link, fromDoc!), result[0] as Doc) || Doc.AreProtosEqual(DocCast(LinkManager.getOppositeAnchor(link, fromDoc!)?.annotationOn), result[0] as Doc)
+                                )
+                                    ? 'bold'
+                                    : '',
+                            }}
                             className={className}>
                             <div className="searchBox-result-title">{title as string}</div>
                             <div className="searchBox-result-type">{formattedType}</div>
@@ -482,7 +496,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                         defaultValue={''}
                         autoComplete="off"
                         onChange={this.onInputChange}
-                        onKeyPress={e => (e.key === 'Enter' ? this.submitSearch() : null)}
+                        onKeyPress={e => {
+                            e.key === 'Enter' ? this.submitSearch() : null;
+                            e.stopPropagation();
+                        }}
                         type="text"
                         placeholder="Search..."
                         id="search-input"
diff --git a/src/fields/util.ts b/src/fields/util.ts
index f222e4555..7f4892bd6 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -449,7 +449,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                     diff?.op === '$addToSet'
                         ? {
                               redo: () => {
-                                  receiver[prop].push(...diff.items.map((item: any) => (item.value ? item.value() : item)));
+                                  receiver[prop].push(...diff.items.map((item: any) => item.value ?? item));
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
                               },
                               undo: action(() => {
@@ -459,7 +459,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                                           const ind = receiver[prop].findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading);
                                           ind !== -1 && receiver[prop].splice(ind, 1);
                                       } else {
-                                          const ind = receiver[prop].indexOf(item.value ? item.value() : item);
+                                          const ind = receiver[prop].indexOf(item.value ?? item);
                                           ind !== -1 && receiver[prop].splice(ind, 1);
                                       }
                                   });
@@ -471,7 +471,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                         ? {
                               redo: action(() => {
                                   diff.items.forEach((item: any) => {
-                                      const ind = item instanceof SchemaHeaderField ? receiver[prop].findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading) : receiver[prop].indexOf(item.value ? item.value() : item);
+                                      const ind = item instanceof SchemaHeaderField ? receiver[prop].findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading) : receiver[prop].indexOf(item.value ?? item);
                                       ind !== -1 && receiver[prop].splice(ind, 1);
                                   });
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
@@ -483,8 +483,8 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
                                           const ind = (prevValue as List<any>).findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading);
                                           ind !== -1 && receiver[prop].findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading) === -1 && receiver[prop].splice(ind, 0, item);
                                       } else {
-                                          const ind = (prevValue as List<any>).indexOf(item.value ? item.value() : item);
-                                          ind !== -1 && receiver[prop].indexOf(item.value ? item.value() : item) === -1 && receiver[prop].splice(ind, 0, item);
+                                          const ind = (prevValue as List<any>).indexOf(item.value ?? item);
+                                          ind !== -1 && receiver[prop].indexOf(item.value ?? item) === -1 && receiver[prop].splice(ind, 0, item);
                                       }
                                   });
                                   lastValue = ObjectField.MakeCopy(receiver[prop]);
-- 
cgit v1.2.3-70-g09d2


From 84f728cffb94319b86be8d6cc478ce424ec45c2f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 20 Jan 2023 11:17:38 -0500
Subject: removed tour map from lightbox.  added option to create anchors
 without adding thm as annotations. made zoom of text an option for pres and
 links

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/DragManager.ts                     |  4 +-
 src/client/util/LinkFollower.ts                    | 10 +---
 src/client/views/DocComponent.tsx                  |  7 +++
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/InkingStroke.tsx                  |  4 +-
 src/client/views/LightboxView.tsx                  | 56 +++++-----------------
 src/client/views/MarqueeAnnotator.tsx              |  8 ++--
 src/client/views/PropertiesView.tsx                | 10 ++++
 .../collections/CollectionStackedTimeline.tsx      | 18 +++----
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTimeView.tsx       | 14 +++---
 src/client/views/collections/TabDocView.tsx        |  2 +-
 .../CollectionFreeFormLinkView.tsx                 |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 12 +++--
 src/client/views/nodes/AudioBox.tsx                |  7 ++-
 src/client/views/nodes/DocumentLinksButton.tsx     |  6 +--
 src/client/views/nodes/DocumentView.tsx            | 14 +++---
 src/client/views/nodes/FunctionPlotBox.tsx         |  7 +--
 src/client/views/nodes/ImageBox.tsx                |  8 ++--
 src/client/views/nodes/LabelBox.tsx                |  4 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  5 +-
 src/client/views/nodes/PDFBox.tsx                  |  8 ++--
 src/client/views/nodes/ScreenshotBox.tsx           |  4 +-
 src/client/views/nodes/VideoBox.tsx                | 11 +++--
 src/client/views/nodes/WebBox.tsx                  |  6 +--
 .../views/nodes/formattedText/FormattedTextBox.tsx | 39 +++++++--------
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/client/views/nodes/trails/PresBox.tsx          | 13 +++--
 src/client/views/pdf/AnchorMenu.tsx                | 13 ++---
 src/client/views/pdf/Annotation.tsx                |  8 ++--
 src/client/views/pdf/PDFViewer.tsx                 |  4 +-
 33 files changed, 157 insertions(+), 159 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b51fcb454..80b040cc0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1310,13 +1310,13 @@ export namespace DocUtils {
         options?.afterFocus?.(false);
     }
 
-    export let ActiveRecordings: { props: FieldViewProps; getAnchor: () => Doc }[] = [];
+    export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = [];
 
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) {
         broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
         return DocUtils.ActiveRecordings.map(audio => {
             const sourceDoc = getSourceDoc();
-            const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
+            const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor(true) || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
             link && (link.followLinkLocation = OpenWhere.addRight);
             return link;
         });
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d0690fa10..a56f87075 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -251,8 +251,8 @@ export namespace DragManager {
     }
 
     // drags a linker button and creates a link on drop
-    export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | (() => Doc), downX: number, downY: number, options?: DragOptions) {
-        StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.() ?? sourceView.rootDoc), downX, downY, options);
+    export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | ((addAsAnnotation: boolean) => Doc), downX: number, downY: number, options?: DragOptions) {
+        StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.rootDoc), downX, downY, options);
     }
 
     // drags a column from a schema view
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 0f216e349..5bdfca54a 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -111,15 +111,9 @@ export class LinkFollower {
                         easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any,
                         effect: sourceDoc,
                         originatingDoc: sourceDoc,
-                        zoomTextSelections: false,
+                        zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText),
                     };
-                    if (target.TourMap) {
-                        const fieldKey = Doc.LayoutFieldKey(target);
-                        const tour = DocListCast(target[fieldKey]).reverse();
-                        LightboxView.SetLightboxDoc(currentContext, undefined, tour);
-                        setTimeout(LightboxView.Next);
-                        allFinished();
-                    } else if (target.type === DocumentType.PRES) {
+                    if (target.type === DocumentType.PRES) {
                         const containerAnnoDoc = Cast(sourceDoc, Doc, null);
                         const containerDoc = containerAnnoDoc || sourceDoc;
                         var containerDocContext = containerDoc?.context ? [Cast(await containerDoc?.context, Doc, null)] : ([] as Doc[]);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index c9c09b63b..78ab2b3d4 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -130,6 +130,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
         isAnyChildContentActive = () => this._isAnyChildContentActive;
 
+        isContentActive = (outsideReaction?: boolean) =>
+            this.props.isContentActive?.() === false
+                ? false
+                : Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.isAnyChildContentActive()
+                ? true
+                : undefined;
+
         lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result;
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 90c6c040c..f06dc93e3 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -501,7 +501,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             key="popup"
                             showPopup={this._showLinkPopup}
                             linkCreated={link => (link.linkDisplay = !this.props.views().lastElement()?.rootDoc.isLinkButton)}
-                            linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.()}
+                            linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
                             linkFrom={() => this.props.views().lastElement()?.rootDoc}
                         />
                     </div>
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index a6fa2f04b..319a9419e 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -78,8 +78,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
     screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.NativeDimScaling?.() || 1);
 
-    getAnchor = () => {
-        return this._subContentView?.getAnchor?.() || this.rootDoc;
+    getAnchor = (addAsAnnotation: boolean) => {
+        return this._subContentView?.getAnchor?.(addAsAnnotation) || this.rootDoc;
     };
 
     scrollFocus = (textAnchor: Doc, options: DocFocusOptions) => {
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index d79b696a3..3627aa783 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -37,7 +37,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     @observable private static _doc: Opt<Doc>;
     @observable private static _docTarget: Opt<Doc>;
     @observable private static _docFilters: string[] = []; // filters
-    @observable private static _tourMap: Opt<Doc[]> = []; // list of all tours available from the current target
     private static _savedState: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }>;
     private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
     @observable private static _future: Opt<Doc[]> = [];
@@ -90,13 +89,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         this._doc = doc;
         this._layoutTemplate = layoutTemplate;
         this._docTarget = target || doc;
-        this._tourMap = DocListCast(doc?.links)
-            .map(link => {
-                const opp = LinkManager.getOppositeAnchor(link, doc!);
-                return opp?.TourMap ? opp : undefined;
-            })
-            .filter(m => m)
-            .map(m => m!);
 
         return true;
     }
@@ -164,7 +156,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         const target = (LightboxView._docTarget = this._future?.pop());
         const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
         if (targetDocView && target) {
-            const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
+            const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement();
             l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
             targetDocView.focus(target, { originalTarget: target, willPanZoom: true, zoomScale: 0.9 });
             if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
@@ -189,13 +181,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 LightboxView.SetLightboxDoc(target);
             }
         }
-        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
-            .map(link => {
-                const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
-                return opp?.TourMap ? opp : undefined;
-            })
-            .filter(m => m)
-            .map(m => m!);
     }
 
     @action public static Previous() {
@@ -214,13 +199,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             LightboxView.SetLightboxDoc(doc, target);
         }
         if (LightboxView._future?.lastElement() !== previous.target || previous.doc) LightboxView._future?.push(previous.target || previous.doc);
-        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
-            .map(link => {
-                const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
-                return opp?.TourMap ? opp : undefined;
-            })
-            .filter(m => m)
-            .map(m => m!);
     }
     @action
     stepInto = () => {
@@ -231,27 +209,20 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             history: LightboxView._history,
             saved: LightboxView._savedState,
         });
-        const tours = LightboxView._tourMap;
-        if (tours && tours.length) {
-            const fieldKey = Doc.LayoutFieldKey(tours[0]);
-            LightboxView._future?.push(...DocListCast(tours[0][fieldKey]).reverse());
-        } else {
-            const coll = LightboxView._docTarget;
-            if (coll) {
-                const fieldKey = Doc.LayoutFieldKey(coll);
-                const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
-                const links = DocListCast(coll.links)
-                    .map(link => LinkManager.getOppositeAnchor(link, coll))
-                    .filter(doc => doc)
-                    .map(doc => doc!);
-                LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
-                TabDocView.PinDoc(coll, { hidePresBox: true });
-            }
+        const coll = LightboxView._docTarget;
+        if (coll) {
+            const fieldKey = Doc.LayoutFieldKey(coll);
+            const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
+            const links = DocListCast(coll.links)
+                .map(link => LinkManager.getOppositeAnchor(link, coll))
+                .filter(doc => doc)
+                .map(doc => doc!);
+            LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
+            TabDocView.PinDoc(coll, { hidePresBox: true });
         }
     };
 
     future = () => LightboxView._future;
-    tourMap = () => LightboxView._tourMap;
     render() {
         let downx = 0,
             downy = 0;
@@ -345,7 +316,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     },
                     this.future()?.length.toString()
                 )}
-                <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} />
+                <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} />
                 <div
                     className="lightboxView-navBtn"
                     title={'toggle fit width'}
@@ -393,7 +364,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
 }
 interface LightboxTourBtnProps {
     navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => JSX.Element;
-    tourMap: () => Opt<Doc[]>;
     future: () => Opt<Doc[]>;
     stepInto: () => void;
 }
@@ -410,7 +380,7 @@ export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> {
                 e.stopPropagation();
                 this.props.stepInto();
             },
-            StrCast(this.props.tourMap()?.lastElement()?.TourMap)
+            ''
         );
     }
 }
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index bf1242346..b1965c5c8 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -52,8 +52,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true));
         AnchorMenu.Instance.OnAudio = unimplementedFunction;
         AnchorMenu.Instance.Highlight = this.highlight;
-        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations);
-        AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor;
+        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, addAsAnnotation);
+        AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
     }
 
     @action
@@ -194,11 +194,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         return textRegionAnno;
     };
     @action
-    highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => {
+    highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
         const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
-        !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
+        addAsAnnotation && !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
         return (annotationDoc as Doc) ?? undefined;
     };
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8d495d286..af50478b0 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1679,6 +1679,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                                 <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                                             </button>
                                         </div>
+                                        <div className="propertiesView-input inline">
+                                            <p>Zoom Text Selections</p>
+                                            <button
+                                                style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
+                                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
+                                                onClick={e => e.stopPropagation()}
+                                                className="propertiesButton">
+                                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                            </button>
+                                        </div>
                                         <div className="propertiesView-input inline">
                                             <p>Toggle Target (Show/Hide)</p>
                                             <button
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index e9bf03208..b7ea26b44 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -190,7 +190,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         CollectionStackedTimeline.SelectingRegion = this;
                     } else {
                         this._markerEnd = this.currentTime;
-                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd, undefined, true);
                         this._markerEnd = undefined;
                         CollectionStackedTimeline.SelectingRegion = undefined;
                     }
@@ -257,7 +257,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         this._markerEnd = tmp;
                     }
                     if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
-                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
+                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd, undefined, true);
                         setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
                     }
                     (!isClick || !wasSelecting) && (this._markerEnd = undefined);
@@ -273,7 +273,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 undefined,
                 () => {
                     if (shiftKey) {
-                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime, undefined, undefined, true);
                     } else {
                         !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
                     }
@@ -388,7 +388,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     // creates marker on timeline
     @undoBatch
     @action
-    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number, docAnchor?: Doc) {
+    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
         if (anchorStartTime === undefined) return rootDoc;
         const anchor =
             docAnchor ??
@@ -407,10 +407,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             });
         Doc.GetProto(anchor)[startTag] = anchorStartTime;
         Doc.GetProto(anchor)[endTag] = anchorEndTime;
-        if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
-            Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
-        } else {
-            dataDoc[fieldKey] = new List<Doc>([anchor]);
+        if (addAsAnnotation) {
+            if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
+                Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
+            } else {
+                dataDoc[fieldKey] = new List<Doc>([anchor]);
+            }
         }
         return anchor;
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d2074219a..27ae3041f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -334,7 +334,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                 const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
                                 const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
                                 if (focusNode) {
-                                    const anchor = srcWeb?.ComponentView?.getAnchor?.();
+                                    const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
                                     anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor });
                                 }
                             }
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index a1466bcd0..8c613198d 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -32,7 +32,7 @@ export class CollectionTimeView extends CollectionSubView() {
     @observable _viewDefDivClick: Opt<ScriptField>;
     @observable _focusPivotField: Opt<string>;
 
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         const anchor = Docs.Create.HTMLAnchorDocument([], {
             title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
             annotationOn: this.rootDoc,
@@ -45,11 +45,13 @@ export class CollectionTimeView extends CollectionSubView() {
         proto.docRangeFilters = ObjectField.MakeCopy(this.layoutDoc._docRangeFilters as ObjectField) || new List<string>([]);
         proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
 
-        // store anchor in annotations list of document (not technically needed since these anchors are never drawn)
-        if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
-            Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
-        } else {
-            this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+        if (addAsAnnotation) {
+            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+            if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
+                Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+            } else {
+                this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+            }
         }
         return anchor;
     };
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5c05b5c13..f6e4b45ee 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -247,7 +247,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 alert('Cannot pin presentation document to itself');
                 return;
             }
-            const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.();
+            const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false);
             const pinDoc = Doc.MakeAlias(anchorDoc ?? doc);
             pinDoc.presentationTargetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 8919b1c01..9811c239b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -290,7 +290,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                     stroke="antiquewhite"
                     strokeWidth="4"
                     className="collectionfreeformlinkview-linkLine"
-                    style={{ pointerEvents: 'all', opacity: this._opacity, stroke, strokeWidth }}
+                    style={{ pointerEvents: 'visibleStroke', opacity: this._opacity, stroke, strokeWidth }}
                     onClick={this.onClickLine}
                     d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
                     markerEnd={link.linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dc0eb69f3..5cf4cb31f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1565,7 +1565,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             : undefined;
     }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         if (this.props.Document.annotationOn) {
             return this.rootDoc;
         }
@@ -1574,10 +1574,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const proto = Doc.GetProto(anchor);
         proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
         proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
-        if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
-            Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
-        } else {
-            this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+        if (addAsAnnotation) {
+            if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
+                Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+            } else {
+                this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+            }
         }
         return anchor;
     };
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index d95668c89..1d59d3356 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -136,7 +136,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return { la1, la2, linkTime };
     }
 
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         return (
             CollectionStackedTimeline.createAnchor(
                 this.rootDoc,
@@ -144,7 +144,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.annotationKey,
                 '_timecodeToShow' /* audioStart */,
                 '_timecodeToHide' /* audioEnd */,
-                this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined)
+                this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
+                undefined,
+                undefined,
+                addAsAnnotation
             ) || this.rootDoc
         );
     };
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 6f3152981..a39e0f65f 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -135,7 +135,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                             DocumentLinksButton.AnnotationId = undefined;
                         } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
                             const sourceDoc = DocumentLinksButton.StartLink;
-                            const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document;
+                            const targetDoc = this.props.View.ComponentView?.getAnchor?.(true) || this.props.View.Document;
                             const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, 'links'); //why is long drag here when this is used for completing links by clicking?
 
                             LinkManager.currentLink = linkDoc;
@@ -183,8 +183,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 DocumentLinksButton.AnnotationUri = undefined;
                 //!this.props.StartLink
             } else if (startLink !== endLink) {
-                endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
-                startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
+                endLink = endLinkView?.docView?._componentView?.getAnchor?.(true) || endLink;
+                startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
                 const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined, undefined, undefined, true);
 
                 LinkManager.currentLink = linkDoc;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1df46488b..054d01d8e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -121,7 +121,7 @@ export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void;
 export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
 export interface DocComponentView {
     updateIcon?: () => void; // updates the icon representation of the document
-    getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
+    getAnchor?: (addAsAnnotation: boolean) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
     brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void;
     setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
@@ -783,9 +783,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @undoBatch
     @action
-    toggleFollowLink = (location: Opt<string>, zoom?: boolean, setPushpin?: boolean): void => {
+    toggleFollowLink = (location: Opt<string>, zoom?: boolean, setTargetToggle?: boolean): void => {
         this.Document.ignoreClick = false;
-        if (setPushpin) {
+        if (setTargetToggle) {
             this.Document.followLinkToggle = !this.Document.followLinkToggle;
             this.Document._isLinkButton = this.Document.followLinkToggle || this.Document._isLinkButton;
         } else {
@@ -854,7 +854,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
             }
             if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
-                const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
+                const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.props.Document;
                 de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
             }
         }
@@ -979,7 +979,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     onClicks.push({ description: this.Document.isLinkButton ? 'Remove Follow Behavior' : 'Follow Link in Place', event: () => this.toggleFollowLink('inPlace', false, false), icon: 'link' });
                     !this.Document.isLinkButton && onClicks.push({ description: 'Follow Link on Right', event: () => this.toggleFollowLink('add:right', false, false), icon: 'link' });
                     onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(undefined, false, false), icon: 'link' });
-                    onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Pushpin', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
+                    onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
                     onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
                     !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
                 } else if (DocListCast(this.Document.links).length) {
@@ -1091,7 +1091,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     onClickFunc = () => this.onClickHandler;
     setHeight = (height: number) => (this.layoutDoc._height = height);
-    setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
+    setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive() === false
             ? false
@@ -1264,7 +1264,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     playAnnotation = () => {
         const self = this;
         const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null);
-        const anno = audioAnnos.lastElement();
+        const anno = audioAnnos?.lastElement();
         if (anno instanceof AudioField && this.audioAnnoState === 'stopped') {
             new Howl({
                 src: [anno.url.href],
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 24562ccbd..5c0005dae 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -12,7 +12,7 @@ import { TraceMobx } from '../../../fields/util';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
-import { ViewBoxBaseComponent } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { DocFocusOptions } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 
@@ -22,7 +22,7 @@ type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSch
 const EquationDocument = makeInterface(EquationSchema, documentSchema);
 
 @observer
-export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
+export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(FunctionPlotBox, fieldKey);
     }
@@ -41,10 +41,11 @@ export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
             () => this.createGraph()
         );
     }
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc });
         anchor.xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
         anchor.yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
+        if (addAsAnnotation) this.addDocument(anchor);
         return anchor;
     };
     @action
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ac953d13b..e2ecca0b6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -52,7 +52,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
-    private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
+    private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
     @observable _curSuffix = '';
     @observable _uploadIcon = uploadIcons.idle;
 
@@ -86,13 +86,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             : undefined;
     }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         const anchor =
-            this._getAnchor?.(this._savedAnnotations) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
+            this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
             Docs.Create.ImageanchorDocument({ presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
         if (anchor) {
             PresBox.pinDocView(anchor, { pinData: { pannable: true, dataview: true, dataannos: true } }, this.rootDoc);
-            this.addDocument(anchor);
+            addAsAnnotation && this.addDocument(anchor);
             return anchor;
         }
         return this.rootDoc;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 10897b48f..a2143f629 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -36,9 +36,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
         this._timeout && clearTimeout(this._timeout);
     }
 
-    getAnchor = () => {
-        return this.rootDoc;
-    };
+    getAnchor = (addAsAnnotation: boolean) => this.rootDoc;
 
     getTitle() {
         return this.rootDoc['title-custom'] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 232c3459e..7eb42994b 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -113,7 +113,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                                     this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
                                 }
                                 this._toolTipText = '';
-                                if (LinkDocPreview.LinkInfo?.noPreview || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
+                                if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink();
                             }
                         })
                     );
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c8d5b0154..95cb49037 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -522,10 +522,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     }
 
-    getAnchor = () => {
-        const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ?? this.rootDoc;
-        return anchor;
-    };
+    getAnchor = (addAsAnnotation: boolean) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
 
     /**
      * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index e22ee5021..71ef9bc84 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -212,7 +212,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         PresBox.restoreTargetDocView(this.props.DocumentView?.(), {}, doc, options.zoomTime ?? 500, { pannable: doc.presPinData ? true : false });
         return this._pdfViewer?.scrollFocus(doc, NumCast(doc.presPinViewScroll, NumCast(doc.y)), options) ?? (didToggle ? 1 : undefined);
     };
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         let ele: Opt<HTMLDivElement> = undefined;
         if (this._pdfViewer?.selectionContent()) {
             ele = document.createElement('div');
@@ -226,10 +226,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
             return anchor;
         };
-        const anchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ?? docAnchor();
+        const anchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations(), false) ?? docAnchor();
         anchor.text = ele?.textContent ?? '';
         anchor.textHtml = ele?.innerHTML;
-        this.addDocument(anchor);
+        if (addAsAnnotation) {
+            this.addDocument(anchor);
+        }
         return anchor;
     };
 
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 76a24d831..f94996c66 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -128,9 +128,9 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             this.setupDictation();
         }
     }
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3) || this.rootDoc;
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
     };
 
     videoLoad = () => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index a8f78edd5..1dfa55c64 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -385,15 +385,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
         Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
         this.props.addDocument?.(imageSummary);
-        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, 'video snapshot');
+        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor(true) }, 'video snapshot');
         link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
         setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true));
     };
 
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
-        const marquee = AnchorMenu.Instance.GetAnchor?.();
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
+        const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
+        return (
+            CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) ||
+            this.rootDoc
+        );
     };
 
     // sets video info on load
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8be4884ce..acf4fe4b0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -307,7 +307,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return undefined;
     };
 
-    getAnchor = () => {
+    getAnchor = (addAsAnnotation: boolean) => {
         let ele: Opt<HTMLDivElement> = undefined;
         try {
             const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents();
@@ -317,7 +317,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         } catch (e) {}
         const anchor =
-            this._getAnchor(this._savedAnnotations) ??
+            this._getAnchor(this._savedAnnotations, false) ??
             Docs.Create.WebanchorDocument(this._url, {
                 title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
                 y: NumCast(this.layoutDoc._scrollTop),
@@ -325,7 +325,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             });
         anchor.text = ele?.textContent ?? '';
         anchor.textHtml = ele?.innerHTML;
-        this.addDocumentWrapper(anchor);
+        addAsAnnotation && this.addDocumentWrapper(anchor);
         return anchor;
     };
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 62e215521..deb1d68a7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -231,7 +231,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     }
 
-    getAnchor = () => this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', false);
+    getAnchor = (addAsAnnotation: boolean) => this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
 
     @action
     setupAnchorMenu = () => {
@@ -239,23 +239,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => {
             !this.layoutDoc.showSidebar && this.toggleSidebar();
-            setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
+            setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
         };
         AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
             !this.layoutDoc.showSidebar && this.toggleSidebar();
-            const anchor = this.getAnchor();
-            const target = this._sidebarRef.current?.anchorMenuClick(anchor);
-            if (target) {
-                anchor.followLinkAudio = true;
-                DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
-                target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
-            }
+            const anchor = this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true, true);
+            setTimeout(() => {
+                const target = this._sidebarRef.current?.anchorMenuClick(anchor);
+                if (target) {
+                    anchor.followLinkAudio = true;
+                    DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
+                    target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
+                }
+            });
         };
         AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
             this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch);
             return undefined;
         });
-        AnchorMenu.Instance.onMakeAnchor = this.getAnchor;
+        AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
         AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
         /**
          * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation.
@@ -270,7 +272,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 return target;
             };
 
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), this.getAnchor, targetCreator), e.pageX, e.pageY);
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
         });
         const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
         this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
@@ -518,7 +520,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = this.getAnchor;
+        if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
         const dragData = de.complete.docDragData;
         if (dragData) {
             const draggedDoc = dragData.draggedDocuments.length && dragData.draggedDocuments[0];
@@ -689,9 +691,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     pinToPres = (anchor: Doc) => this.props.pinToPres(anchor, {});
 
     @undoBatch
-    makePushpin = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle);
+    makeTargetToggle = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle);
 
-    isPushpin = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
+    isTargetToggler = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
@@ -714,8 +716,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         AnchorMenu.Instance.Delete = () => this.deleteAnnotation(anchor as Doc);
                         AnchorMenu.Instance.Pinned = false;
                         AnchorMenu.Instance.PinToPres = () => this.pinToPres(anchor as Doc);
-                        AnchorMenu.Instance.MakePushpin = () => this.makePushpin(anchor as Doc);
-                        AnchorMenu.Instance.IsPushpin = () => this.isPushpin(anchor as Doc);
+                        AnchorMenu.Instance.MakeTargetToggle = () => this.makeTargetToggle(anchor as Doc);
+                        AnchorMenu.Instance.IsTargetToggler = () => this.isTargetToggler(anchor as Doc);
                         AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
                     })
                 );
@@ -891,7 +893,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     // TODO: nda -- Look at how link anchors are added
-    makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string, noPreview?: boolean) {
+    makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string, noPreview?: boolean, addAsAnnotation?: boolean) {
         const state = this._editorView?.state;
         if (state) {
             let selectedText = '';
@@ -901,7 +903,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             if (sel.from !== sel.to) {
                 const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: '#' + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true });
                 const href = targetHref ?? Doc.localServerPath(anchor);
-                if (anchor !== anchorDoc) this.addDocument(anchor);
+                if (anchor !== anchorDoc && addAsAnnotation) this.addDocument(anchor);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
                     if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
                         const allAnchors = [{ href, title, anchorId: anchor[Id] }];
@@ -1476,7 +1478,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
             while (target && !target.dataset?.targethrefs) target = target.parentElement;
             FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
-            if (target) return;
         }
 
         if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index e3c67ad2e..5675776fb 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -259,7 +259,7 @@ export class RichTextRules {
                                 this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
                             }
                             const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
-                            DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, 'portal to:portal from', undefined);
+                            DocUtils.MakeLink({ doc: this.TextBox.getAnchor(true) }, { doc: target }, 'portal to:portal from', undefined);
 
                             const fstate = this.TextBox.EditorView?.state;
                             if (fstate && selection) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 855a7f171..226fd8d7d 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -104,7 +104,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return NumCast(this.rootDoc._itemIndex);
     }
     @computed get activeItem() {
-        return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null);
+        return DocCast(this.childDocs[NumCast(this.rootDoc._itemIndex)]);
     }
     @computed get targetDoc() {
         return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
@@ -452,7 +452,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             pinDoc.presWidth = NumCast(targetDoc.width);
             pinDoc.presHeight = NumCast(targetDoc.height);
         }
-        if (pinProps.pinAudioPlay) pinDoc.followLinkAudio = true;
+        if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true;
         if (pinProps.pinData) {
             pinDoc.presPinData =
                 pinProps.pinData.scrollable ||
@@ -577,7 +577,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     noSelect: true,
                     originatingDoc: activeItem,
                     easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
-                    zoomTextSelections: true,
+                    zoomTextSelections: BoolCast(activeItem.presZoomText),
+                    playAudio: BoolCast(activeItem.presPlayAudio),
                 };
                 if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
                 var containerDocContext = srcContext ? [srcContext] : [];
@@ -1416,7 +1417,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             Effects
                             <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
                                 <div className="presBox-subheading">Play Audio Annotation</div>
-                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.followLinkAudio = !BoolCast(activeItem.followLinkAudio))} checked={BoolCast(activeItem.followLinkAudio)} />
+                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} />
+                            </div>
+                            <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+                                <div className="presBox-subheading">Zoom Text Selections</div>
+                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />
                             </div>
                             <div
                                 className="presBox-dropdown"
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 265328036..2fb795b06 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -11,6 +11,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
 import { LinkPopup } from '../linking/LinkPopup';
 import { ButtonDropdown } from '../nodes/formattedText/RichTextMenu';
 import './AnchorMenu.scss';
+import { truncate } from 'lodash';
 
 @observer
 export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -52,13 +53,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
     public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
-    public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined;
-    public GetAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
+    public Highlight: (color: string, isTargetToggler: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
+    public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
     public Delete: () => void = unimplementedFunction;
     public AddTag: (key: string, value: string) => boolean = returnFalse;
     public PinToPres: () => void = unimplementedFunction;
-    public MakePushpin: () => void = unimplementedFunction;
-    public IsPushpin: () => boolean = returnFalse;
+    public MakeTargetToggle: () => void = unimplementedFunction;
+    public IsTargetToggler: () => boolean = returnFalse;
     public get Active() {
         return this._left > 0;
     }
@@ -238,8 +239,8 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                               <FontAwesomeIcon icon="map-pin" size="lg" />
                           </button>
                       </Tooltip>,
-                      <Tooltip key="pushpin" title={<div className="dash-tooltip">{'toggle pushpin behavior'}</div>}>
-                          <button className="antimodeMenu-button" style={{ color: this.IsPushpin() ? 'black' : 'white', backgroundColor: this.IsPushpin() ? 'white' : 'black' }} onPointerDown={this.MakePushpin}>
+                      <Tooltip key="toggle" title={<div className="dash-tooltip">{'make target visibility toggle on click'}</div>}>
+                          <button className="antimodeMenu-button" style={{ color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }} onPointerDown={this.MakeTargetToggle}>
                               <FontAwesomeIcon icon="thumbtack" size="lg" />
                           </button>
                       </Tooltip>,
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 7069ff399..d8e44ae9d 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -55,9 +55,9 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     pinToPres = () => this.props.pinToPres(this.annoTextRegion, {});
 
     @undoBatch
-    makePushpin = () => (this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle);
+    makeTargretToggle = () => (this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle);
 
-    isPushpin = () => BoolCast(this.annoTextRegion.followLinkToggle);
+    isTargetToggler = () => BoolCast(this.annoTextRegion.followLinkToggle);
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
@@ -67,8 +67,8 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             AnchorMenu.Instance.Pinned = false;
             AnchorMenu.Instance.AddTag = this.addTag.bind(this);
             AnchorMenu.Instance.PinToPres = this.pinToPres;
-            AnchorMenu.Instance.MakePushpin = this.makePushpin;
-            AnchorMenu.Instance.IsPushpin = this.isPushpin;
+            AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
+            AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
             AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
             e.stopPropagation();
         } else if (e.button === 0) {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f95d5ac2e..b0b7816b8 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -135,7 +135,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     copy = (e: ClipboardEvent) => {
         if (this.props.isContentActive() && e.clipboardData) {
             e.clipboardData.setData('text/plain', this._selectionText);
-            const anchor = this._getAnchor();
+            const anchor = this._getAnchor(undefined, false);
             if (anchor) {
                 anchor.textCopied = true;
                 e.clipboardData.setData('dash/pdfAnchor', anchor[Id]);
@@ -317,7 +317,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this._ignoreScroll = false;
             if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio
             this._scrollTimer = setTimeout(() => {
-                DocUtils.MakeLinkToActiveAudio(() => this.props.DocumentView?.().ComponentView?.getAnchor!()!, false);
+                DocUtils.MakeLinkToActiveAudio(() => this.props.DocumentView?.().ComponentView?.getAnchor!(true)!, false);
                 this._scrollTimer = undefined;
             }, 200);
         }
-- 
cgit v1.2.3-70-g09d2


From 555d67a31adbe7e6d42f7f7805ba1348e6d505f6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 Jan 2023 17:05:25 -0500
Subject: stopped storing  presCollection in PresBox in favor of computing it. 
 added anchor menu option for viewing linked trail.  fixed showing slected
 slides in presbox when link with up is used.

---
 package-lock.json                                  | 1621 ++++++++------------
 package.json                                       |    2 +-
 src/client/util/DocumentManager.ts                 |   26 +-
 src/client/views/AntimodeMenu.tsx                  |  143 +-
 src/client/views/DocumentButtonBar.tsx             |   30 +-
 src/client/views/MainView.tsx                      |    1 +
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |   60 +-
 src/client/views/nodes/DocumentView.tsx            |    4 +
 src/client/views/nodes/button/FontIconBox.tsx      |    2 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |    6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   12 +-
 src/client/views/nodes/formattedText/marks_rts.ts  |    2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  307 ++--
 src/client/views/nodes/trails/PresElementBox.tsx   |   10 +-
 src/client/views/pdf/AnchorMenu.tsx                |  145 +-
 src/client/views/pdf/Annotation.tsx                |    7 -
 src/client/views/search/SearchBox.tsx              |   13 +-
 17 files changed, 1058 insertions(+), 1333 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 26e7a41d7..547a6e1d7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2288,7 +2288,7 @@
         "@types/strip-bom": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
-            "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
+            "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
             "dev": true
         },
         "@types/strip-json-comments": {
@@ -2639,7 +2639,7 @@
                 "textarea-caret": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
-                    "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
+                    "integrity": "sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg=="
                 }
             }
         },
@@ -2738,7 +2738,7 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
         },
         "agent-base": {
             "version": "6.0.2",
@@ -3530,7 +3530,7 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
         },
         "bail": {
             "version": "2.0.2",
@@ -3600,7 +3600,7 @@
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
         },
         "base64-js": {
             "version": "1.5.1",
@@ -4577,7 +4577,7 @@
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -4587,7 +4587,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
         },
         "compress-brotli": {
             "version": "1.3.8",
@@ -5295,7 +5295,7 @@
         "custom-event": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+            "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
         },
         "cyclist": {
             "version": "1.0.1",
@@ -6211,7 +6211,7 @@
         "dynamic-dedupe": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
-            "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==",
+            "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
             "dev": true,
             "requires": {
                 "xtend": "^4.0.0"
@@ -6341,7 +6341,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -8711,7 +8711,7 @@
         "fs-extra": {
             "version": "0.26.7",
             "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
-            "integrity": "sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==",
+            "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
             "requires": {
                 "graceful-fs": "^4.1.2",
                 "jsonfile": "^2.1.0",
@@ -9349,14 +9349,14 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
         },
         "has-flag": {
             "version": "3.0.0",
@@ -9949,7 +9949,7 @@
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
         },
         "inflight": {
             "version": "1.0.6",
@@ -11002,7 +11002,7 @@
         "jsonfile": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
-            "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
+            "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
             "requires": {
                 "graceful-fs": "^4.1.6"
             }
@@ -11426,7 +11426,7 @@
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
@@ -11687,7 +11687,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -11695,7 +11695,7 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
                 }
             }
         },
@@ -13234,9 +13234,9 @@
             "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
         },
         "npm": {
-            "version": "6.14.17",
-            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.17.tgz",
-            "integrity": "sha512-CxEDn1ydVRPDl4tHrlnq+WevYAhv4GF2AEHzJKQ4prZDZ96IS3Uo6t0Sy6O9kB6XzqkI+J00WfYCqqk0p6IJ1Q==",
+            "version": "6.14.18",
+            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.18.tgz",
+            "integrity": "sha512-p3SjqSchSuNQUqbJBgwdv0L3O6bKkaSfQrQzJsskNpNKLg0g37c5xTXFV0SqTlX9GWvoGxBELVJMRWq0J8oaLA==",
             "requires": {
                 "JSONStream": "^1.3.5",
                 "abbrev": "~1.1.1",
@@ -13245,9 +13245,9 @@
                 "aproba": "^2.0.0",
                 "archy": "~1.0.0",
                 "bin-links": "^1.1.8",
-                "bluebird": "^3.5.5",
+                "bluebird": "^3.7.2",
                 "byte-size": "^5.0.1",
-                "cacache": "^12.0.3",
+                "cacache": "^12.0.4",
                 "call-limit": "^1.1.1",
                 "chownr": "^1.1.4",
                 "ci-info": "^2.0.0",
@@ -13255,19 +13255,19 @@
                 "cli-table3": "^0.5.1",
                 "cmd-shim": "^3.0.3",
                 "columnify": "~1.5.4",
-                "config-chain": "^1.1.12",
+                "config-chain": "^1.1.13",
                 "debuglog": "*",
                 "detect-indent": "~5.0.0",
                 "detect-newline": "^2.1.0",
-                "dezalgo": "~1.0.3",
+                "dezalgo": "^1.0.4",
                 "editor": "~1.0.0",
-                "figgy-pudding": "^3.5.1",
+                "figgy-pudding": "^3.5.2",
                 "find-npm-prefix": "^1.0.2",
                 "fs-vacuum": "~1.2.10",
                 "fs-write-stream-atomic": "~1.0.10",
                 "gentle-fs": "^2.3.1",
-                "glob": "^7.1.6",
-                "graceful-fs": "^4.2.4",
+                "glob": "^7.2.3",
+                "graceful-fs": "^4.2.10",
                 "has-unicode": "~2.0.1",
                 "hosted-git-info": "^2.8.9",
                 "iferr": "^1.0.2",
@@ -13277,7 +13277,7 @@
                 "inherits": "^2.0.4",
                 "ini": "^1.3.8",
                 "init-package-json": "^1.10.3",
-                "is-cidr": "^3.0.0",
+                "is-cidr": "^3.1.1",
                 "json-parse-better-errors": "^1.0.2",
                 "lazy-property": "~1.0.0",
                 "libcipm": "^4.0.8",
@@ -13288,7 +13288,7 @@
                 "libnpmsearch": "^2.0.2",
                 "libnpmteam": "^1.0.2",
                 "libnpx": "^10.2.4",
-                "lock-verify": "^2.1.0",
+                "lock-verify": "^2.2.2",
                 "lockfile": "^1.0.4",
                 "lodash._baseindexof": "*",
                 "lodash._baseuniq": "~4.6.0",
@@ -13302,11 +13302,11 @@
                 "lodash.uniq": "~4.5.0",
                 "lodash.without": "~4.4.0",
                 "lru-cache": "^5.1.1",
-                "meant": "^1.0.2",
+                "meant": "^1.0.3",
                 "mississippi": "^3.0.0",
-                "mkdirp": "^0.5.5",
+                "mkdirp": "^0.5.6",
                 "move-concurrently": "^1.0.1",
-                "node-gyp": "^5.1.0",
+                "node-gyp": "^5.1.1",
                 "nopt": "^4.0.3",
                 "normalize-package-data": "^2.5.0",
                 "npm-audit-report": "^1.3.3",
@@ -13327,19 +13327,19 @@
                 "path-is-inside": "~1.0.2",
                 "promise-inflight": "~1.0.1",
                 "qrcode-terminal": "^0.12.0",
-                "query-string": "^6.8.2",
-                "qw": "~1.0.1",
+                "query-string": "^6.14.1",
+                "qw": "^1.0.2",
                 "read": "~1.0.7",
                 "read-cmd-shim": "^1.0.5",
                 "read-installed": "~4.0.3",
-                "read-package-json": "^2.1.1",
+                "read-package-json": "^2.1.2",
                 "read-package-tree": "^5.3.1",
                 "readable-stream": "^3.6.0",
                 "readdir-scoped-modules": "^1.1.0",
-                "request": "^2.88.0",
+                "request": "^2.88.2",
                 "retry": "^0.12.0",
                 "rimraf": "^2.7.1",
-                "safe-buffer": "^5.1.2",
+                "safe-buffer": "^5.2.1",
                 "semver": "^5.7.1",
                 "sha": "^3.0.0",
                 "slide": "~1.1.6",
@@ -13355,7 +13355,7 @@
                 "unique-filename": "^1.1.1",
                 "unpipe": "~1.0.0",
                 "update-notifier": "^2.5.0",
-                "uuid": "^3.3.3",
+                "uuid": "^3.4.0",
                 "validate-npm-package-license": "^3.0.4",
                 "validate-npm-package-name": "~3.0.0",
                 "which": "^1.3.1",
@@ -13363,6 +13363,14 @@
                 "write-file-atomic": "^2.4.3"
             },
             "dependencies": {
+                "@iarna/cli": {
+                    "version": "2.1.0",
+                    "bundled": true,
+                    "requires": {
+                        "glob": "^7.1.2",
+                        "signal-exit": "^3.0.2"
+                    }
+                },
                 "JSONStream": {
                     "version": "1.3.5",
                     "bundled": true,
@@ -13373,70 +13381,59 @@
                 },
                 "abbrev": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-                    "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+                    "bundled": true
                 },
                 "agent-base": {
                     "version": "4.3.0",
-                    "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
-                    "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+                    "bundled": true,
                     "requires": {
                         "es6-promisify": "^5.0.0"
                     }
                 },
                 "agentkeepalive": {
                     "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
-                    "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
+                    "bundled": true,
                     "requires": {
                         "humanize-ms": "^1.2.1"
                     }
                 },
                 "ansi-align": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
-                    "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^2.0.0"
                     }
                 },
                 "ansi-regex": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+                    "bundled": true
                 },
                 "ansi-styles": {
                     "version": "3.2.1",
-                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-                    "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+                    "bundled": true,
                     "requires": {
                         "color-convert": "^1.9.0"
                     }
                 },
                 "ansicolors": {
                     "version": "0.3.2",
-                    "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
-                    "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="
+                    "bundled": true
                 },
                 "ansistyles": {
                     "version": "0.1.3",
-                    "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz",
-                    "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk="
+                    "bundled": true
                 },
                 "aproba": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-                    "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+                    "bundled": true
                 },
                 "archy": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
-                    "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
+                    "bundled": true
                 },
                 "are-we-there-yet": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
-                    "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+                    "bundled": true,
                     "requires": {
                         "delegates": "^1.0.0",
                         "readable-stream": "^2.0.6"
@@ -13444,8 +13441,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -13454,69 +13450,70 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "asap": {
                     "version": "2.0.6",
-                    "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
-                    "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+                    "bundled": true
                 },
                 "asn1": {
-                    "version": "0.2.4",
-                    "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-                    "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+                    "version": "0.2.6",
+                    "bundled": true,
                     "requires": {
                         "safer-buffer": "~2.1.0"
                     }
                 },
                 "assert-plus": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-                    "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+                    "bundled": true
                 },
                 "asynckit": {
                     "version": "0.4.0",
-                    "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-                    "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+                    "bundled": true
                 },
                 "aws-sign2": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-                    "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+                    "bundled": true
                 },
                 "aws4": {
-                    "version": "1.8.0",
-                    "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-                    "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
+                    "version": "1.11.0",
+                    "bundled": true
                 },
                 "balanced-match": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-                    "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+                    "version": "1.0.2",
+                    "bundled": true
                 },
                 "bcrypt-pbkdf": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-                    "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
-                    "optional": true,
+                    "bundled": true,
                     "requires": {
                         "tweetnacl": "^0.14.3"
                     }
                 },
                 "bin-links": {
                     "version": "1.1.8",
-                    "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-1.1.8.tgz",
-                    "integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==",
+                    "bundled": true,
                     "requires": {
                         "bluebird": "^3.5.3",
                         "cmd-shim": "^3.0.0",
@@ -13527,14 +13524,12 @@
                     }
                 },
                 "bluebird": {
-                    "version": "3.5.5",
-                    "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
-                    "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w=="
+                    "version": "3.7.2",
+                    "bundled": true
                 },
                 "boxen": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
-                    "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
+                    "bundled": true,
                     "requires": {
                         "ansi-align": "^2.0.0",
                         "camelcase": "^4.0.0",
@@ -13547,8 +13542,7 @@
                 },
                 "brace-expansion": {
                     "version": "1.1.11",
-                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-                    "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+                    "bundled": true,
                     "requires": {
                         "balanced-match": "^1.0.0",
                         "concat-map": "0.0.1"
@@ -13556,28 +13550,23 @@
                 },
                 "buffer-from": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
-                    "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA=="
+                    "bundled": true
                 },
                 "builtins": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
-                    "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og="
+                    "bundled": true
                 },
                 "byline": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz",
-                    "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE="
+                    "bundled": true
                 },
                 "byte-size": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz",
-                    "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw=="
+                    "bundled": true
                 },
                 "cacache": {
-                    "version": "12.0.3",
-                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
-                    "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
+                    "version": "12.0.4",
+                    "bundled": true,
                     "requires": {
                         "bluebird": "^3.5.5",
                         "chownr": "^1.1.1",
@@ -13598,28 +13587,23 @@
                 },
                 "call-limit": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/call-limit/-/call-limit-1.1.1.tgz",
-                    "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ=="
+                    "bundled": true
                 },
                 "camelcase": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+                    "bundled": true
                 },
                 "capture-stack-trace": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
-                    "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
+                    "bundled": true
                 },
                 "caseless": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-                    "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+                    "bundled": true
                 },
                 "chalk": {
                     "version": "2.4.1",
-                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-                    "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+                    "bundled": true,
                     "requires": {
                         "ansi-styles": "^3.2.1",
                         "escape-string-regexp": "^1.0.5",
@@ -13628,31 +13612,26 @@
                 },
                 "chownr": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+                    "bundled": true
                 },
                 "ci-info": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
-                    "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
+                    "bundled": true
                 },
                 "cidr-regex": {
                     "version": "2.0.10",
-                    "resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-2.0.10.tgz",
-                    "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==",
+                    "bundled": true,
                     "requires": {
                         "ip-regex": "^2.1.0"
                     }
                 },
                 "cli-boxes": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
-                    "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
+                    "bundled": true
                 },
                 "cli-columns": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz",
-                    "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^2.0.0",
                         "strip-ansi": "^3.0.1"
@@ -13660,8 +13639,7 @@
                 },
                 "cli-table3": {
                     "version": "0.5.1",
-                    "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
-                    "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
+                    "bundled": true,
                     "requires": {
                         "colors": "^1.1.2",
                         "object-assign": "^4.1.0",
@@ -13670,8 +13648,7 @@
                 },
                 "cliui": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-                    "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^3.1.0",
                         "strip-ansi": "^5.2.0",
@@ -13680,18 +13657,15 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "4.1.1",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                            "bundled": true
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "3.1.0",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-                            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                            "bundled": true,
                             "requires": {
                                 "emoji-regex": "^7.0.1",
                                 "is-fullwidth-code-point": "^2.0.0",
@@ -13700,8 +13674,7 @@
                         },
                         "strip-ansi": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-                            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^4.1.0"
                             }
@@ -13710,13 +13683,11 @@
                 },
                 "clone": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
-                    "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
+                    "bundled": true
                 },
                 "cmd-shim": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-3.0.3.tgz",
-                    "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "mkdirp": "~0.5.0"
@@ -13724,54 +13695,46 @@
                 },
                 "code-point-at": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-                    "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+                    "bundled": true
                 },
                 "color-convert": {
                     "version": "1.9.1",
-                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
-                    "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+                    "bundled": true,
                     "requires": {
                         "color-name": "^1.1.1"
                     }
                 },
                 "color-name": {
                     "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-                    "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+                    "bundled": true
                 },
                 "colors": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
-                    "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
+                    "bundled": true,
                     "optional": true
                 },
                 "columnify": {
                     "version": "1.5.4",
-                    "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz",
-                    "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=",
+                    "bundled": true,
                     "requires": {
                         "strip-ansi": "^3.0.0",
                         "wcwidth": "^1.0.0"
                     }
                 },
                 "combined-stream": {
-                    "version": "1.0.6",
-                    "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
-                    "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+                    "version": "1.0.8",
+                    "bundled": true,
                     "requires": {
                         "delayed-stream": "~1.0.0"
                     }
                 },
                 "concat-map": {
                     "version": "0.0.1",
-                    "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-                    "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+                    "bundled": true
                 },
                 "concat-stream": {
                     "version": "1.6.2",
-                    "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-                    "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+                    "bundled": true,
                     "requires": {
                         "buffer-from": "^1.0.0",
                         "inherits": "^2.0.3",
@@ -13781,8 +13744,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -13791,22 +13753,32 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "config-chain": {
-                    "version": "1.1.12",
-                    "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
-                    "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
+                    "version": "1.1.13",
+                    "bundled": true,
                     "requires": {
                         "ini": "^1.3.4",
                         "proto-list": "~1.2.1"
@@ -13814,8 +13786,7 @@
                 },
                 "configstore": {
                     "version": "3.1.5",
-                    "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz",
-                    "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==",
+                    "bundled": true,
                     "requires": {
                         "dot-prop": "^4.2.1",
                         "graceful-fs": "^4.1.2",
@@ -13827,13 +13798,11 @@
                 },
                 "console-control-strings": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-                    "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+                    "bundled": true
                 },
                 "copy-concurrently": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
-                    "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.1",
                         "fs-write-stream-atomic": "^1.0.8",
@@ -13845,33 +13814,28 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-                            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
+                            "bundled": true
                         }
                     }
                 },
                 "core-util-is": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-                    "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+                    "bundled": true
                 },
                 "create-error-class": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
-                    "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+                    "bundled": true,
                     "requires": {
                         "capture-stack-trace": "^1.0.0"
                     }
                 },
                 "cross-spawn": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-                    "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+                    "bundled": true,
                     "requires": {
                         "lru-cache": "^4.0.1",
                         "shebang-command": "^1.2.0",
@@ -13880,8 +13844,7 @@
                     "dependencies": {
                         "lru-cache": {
                             "version": "4.1.5",
-                            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-                            "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+                            "bundled": true,
                             "requires": {
                                 "pseudomap": "^1.0.2",
                                 "yallist": "^2.1.2"
@@ -13889,104 +13852,87 @@
                         },
                         "yallist": {
                             "version": "2.1.2",
-                            "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-                            "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+                            "bundled": true
                         }
                     }
                 },
                 "crypto-random-string": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
-                    "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
+                    "bundled": true
                 },
                 "cyclist": {
                     "version": "0.2.2",
-                    "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
-                    "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
+                    "bundled": true
                 },
                 "dashdash": {
                     "version": "1.14.1",
-                    "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-                    "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0"
                     }
                 },
                 "debug": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+                    "bundled": true,
                     "requires": {
                         "ms": "2.0.0"
                     },
                     "dependencies": {
                         "ms": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                            "bundled": true
                         }
                     }
                 },
                 "debuglog": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
-                    "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI="
+                    "bundled": true
                 },
                 "decamelize": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-                    "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+                    "bundled": true
                 },
                 "decode-uri-component": {
-                    "version": "0.2.0",
-                    "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-                    "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+                    "version": "0.2.2",
+                    "bundled": true
                 },
                 "deep-extend": {
                     "version": "0.6.0",
-                    "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
-                    "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+                    "bundled": true
                 },
                 "defaults": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
-                    "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+                    "bundled": true,
                     "requires": {
                         "clone": "^1.0.2"
                     }
                 },
                 "define-properties": {
                     "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-                    "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+                    "bundled": true,
                     "requires": {
                         "object-keys": "^1.0.12"
                     }
                 },
                 "delayed-stream": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-                    "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+                    "bundled": true
                 },
                 "delegates": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-                    "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+                    "bundled": true
                 },
                 "detect-indent": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz",
-                    "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50="
+                    "bundled": true
                 },
                 "detect-newline": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
-                    "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I="
+                    "bundled": true
                 },
                 "dezalgo": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
-                    "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
+                    "version": "1.0.4",
+                    "bundled": true,
                     "requires": {
                         "asap": "^2.0.0",
                         "wrappy": "1"
@@ -13994,26 +13940,22 @@
                 },
                 "dot-prop": {
                     "version": "4.2.1",
-                    "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
-                    "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
+                    "bundled": true,
                     "requires": {
                         "is-obj": "^1.0.0"
                     }
                 },
                 "dotenv": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
-                    "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
+                    "bundled": true
                 },
                 "duplexer3": {
                     "version": "0.1.4",
-                    "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
-                    "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
+                    "bundled": true
                 },
                 "duplexify": {
                     "version": "3.6.0",
-                    "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
-                    "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
+                    "bundled": true,
                     "requires": {
                         "end-of-stream": "^1.0.0",
                         "inherits": "^2.0.1",
@@ -14023,8 +13965,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -14033,23 +13974,32 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "ecc-jsbn": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-                    "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
-                    "optional": true,
+                    "bundled": true,
                     "requires": {
                         "jsbn": "~0.1.0",
                         "safer-buffer": "^2.1.0"
@@ -14057,52 +14007,44 @@
                 },
                 "editor": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz",
-                    "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I="
+                    "bundled": true
                 },
                 "emoji-regex": {
                     "version": "7.0.3",
-                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-                    "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
+                    "bundled": true
                 },
                 "encoding": {
                     "version": "0.1.12",
-                    "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
-                    "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+                    "bundled": true,
                     "requires": {
                         "iconv-lite": "~0.4.13"
                     }
                 },
                 "end-of-stream": {
                     "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
-                    "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+                    "bundled": true,
                     "requires": {
                         "once": "^1.4.0"
                     }
                 },
                 "env-paths": {
-                    "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz",
-                    "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA=="
+                    "version": "2.2.1",
+                    "bundled": true
                 },
                 "err-code": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
-                    "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA="
+                    "bundled": true
                 },
                 "errno": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
-                    "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+                    "bundled": true,
                     "requires": {
                         "prr": "~1.0.1"
                     }
                 },
                 "es-abstract": {
                     "version": "1.12.0",
-                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
-                    "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
+                    "bundled": true,
                     "requires": {
                         "es-to-primitive": "^1.1.1",
                         "function-bind": "^1.1.1",
@@ -14113,8 +14055,7 @@
                 },
                 "es-to-primitive": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
-                    "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+                    "bundled": true,
                     "requires": {
                         "is-callable": "^1.1.4",
                         "is-date-object": "^1.0.1",
@@ -14123,26 +14064,22 @@
                 },
                 "es6-promise": {
                     "version": "4.2.8",
-                    "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
-                    "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+                    "bundled": true
                 },
                 "es6-promisify": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
-                    "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+                    "bundled": true,
                     "requires": {
                         "es6-promise": "^4.0.3"
                     }
                 },
                 "escape-string-regexp": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-                    "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+                    "bundled": true
                 },
                 "execa": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
-                    "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+                    "bundled": true,
                     "requires": {
                         "cross-spawn": "^5.0.1",
                         "get-stream": "^3.0.0",
@@ -14155,40 +14092,37 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-                            "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+                            "bundled": true
                         }
                     }
                 },
                 "extend": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-                    "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+                    "bundled": true
                 },
                 "extsprintf": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-                    "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+                    "bundled": true
                 },
                 "fast-json-stable-stringify": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-                    "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+                    "bundled": true
                 },
                 "figgy-pudding": {
-                    "version": "3.5.1",
-                    "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
-                    "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w=="
+                    "version": "3.5.2",
+                    "bundled": true
+                },
+                "filter-obj": {
+                    "version": "1.1.0",
+                    "bundled": true
                 },
                 "find-npm-prefix": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz",
-                    "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA=="
+                    "bundled": true
                 },
                 "flush-write-stream": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
-                    "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
+                    "bundled": true,
                     "requires": {
                         "inherits": "^2.0.1",
                         "readable-stream": "^2.0.4"
@@ -14196,8 +14130,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -14206,37 +14139,45 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "forever-agent": {
                     "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-                    "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+                    "bundled": true
                 },
                 "form-data": {
-                    "version": "2.3.2",
-                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
-                    "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+                    "version": "2.3.3",
+                    "bundled": true,
                     "requires": {
                         "asynckit": "^0.4.0",
-                        "combined-stream": "1.0.6",
+                        "combined-stream": "^1.0.6",
                         "mime-types": "^2.1.12"
                     }
                 },
                 "from2": {
                     "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-                    "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+                    "bundled": true,
                     "requires": {
                         "inherits": "^2.0.1",
                         "readable-stream": "^2.0.0"
@@ -14244,8 +14185,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -14254,30 +14194,39 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "fs-minipass": {
                     "version": "1.2.7",
-                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
-                    "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
+                    "bundled": true,
                     "requires": {
                         "minipass": "^2.6.0"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -14287,8 +14236,7 @@
                 },
                 "fs-vacuum": {
                     "version": "1.2.10",
-                    "resolved": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz",
-                    "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "path-is-inside": "^1.0.1",
@@ -14297,8 +14245,7 @@
                 },
                 "fs-write-stream-atomic": {
                     "version": "1.0.10",
-                    "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
-                    "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "iferr": "^0.1.5",
@@ -14308,13 +14255,11 @@
                     "dependencies": {
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-                            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
+                            "bundled": true
                         },
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -14323,32 +14268,40 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "fs.realpath": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-                    "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+                    "bundled": true
                 },
                 "function-bind": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-                    "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+                    "bundled": true
                 },
                 "gauge": {
                     "version": "2.7.4",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-                    "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.0.3",
                         "console-control-strings": "^1.0.0",
@@ -14362,13 +14315,11 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                            "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                            "bundled": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -14379,13 +14330,11 @@
                 },
                 "genfun": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz",
-                    "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA=="
+                    "bundled": true
                 },
                 "gentle-fs": {
                     "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/gentle-fs/-/gentle-fs-2.3.1.tgz",
-                    "integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.2",
                         "chownr": "^1.1.2",
@@ -14402,62 +14351,63 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-                            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
+                            "bundled": true
                         }
                     }
                 },
                 "get-caller-file": {
                     "version": "2.0.5",
-                    "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-                    "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+                    "bundled": true
                 },
                 "get-stream": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
-                    "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+                    "bundled": true,
                     "requires": {
                         "pump": "^3.0.0"
                     }
                 },
                 "getpass": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-                    "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0"
                     }
                 },
                 "glob": {
-                    "version": "7.1.6",
-                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-                    "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+                    "version": "7.2.3",
+                    "bundled": true,
                     "requires": {
                         "fs.realpath": "^1.0.0",
                         "inflight": "^1.0.4",
                         "inherits": "2",
-                        "minimatch": "^3.0.4",
+                        "minimatch": "^3.1.1",
                         "once": "^1.3.0",
                         "path-is-absolute": "^1.0.0"
+                    },
+                    "dependencies": {
+                        "minimatch": {
+                            "version": "3.1.2",
+                            "bundled": true,
+                            "requires": {
+                                "brace-expansion": "^1.1.7"
+                            }
+                        }
                     }
                 },
                 "global-dirs": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
-                    "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+                    "bundled": true,
                     "requires": {
                         "ini": "^1.3.4"
                     }
                 },
                 "got": {
                     "version": "6.7.1",
-                    "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
-                    "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+                    "bundled": true,
                     "requires": {
                         "create-error-class": "^3.0.0",
                         "duplexer3": "^0.1.4",
@@ -14474,25 +14424,21 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-                            "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+                            "bundled": true
                         }
                     }
                 },
                 "graceful-fs": {
-                    "version": "4.2.4",
-                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
-                    "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
+                    "version": "4.2.10",
+                    "bundled": true
                 },
                 "har-schema": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-                    "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+                    "bundled": true
                 },
                 "har-validator": {
                     "version": "5.1.5",
-                    "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
-                    "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+                    "bundled": true,
                     "requires": {
                         "ajv": "^6.12.3",
                         "har-schema": "^2.0.0"
@@ -14500,8 +14446,7 @@
                     "dependencies": {
                         "ajv": {
                             "version": "6.12.6",
-                            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-                            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+                            "bundled": true,
                             "requires": {
                                 "fast-deep-equal": "^3.1.1",
                                 "fast-json-stable-stringify": "^2.0.0",
@@ -14511,53 +14456,44 @@
                         },
                         "fast-deep-equal": {
                             "version": "3.1.3",
-                            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-                            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                            "bundled": true
                         },
                         "json-schema-traverse": {
                             "version": "0.4.1",
-                            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-                            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+                            "bundled": true
                         }
                     }
                 },
                 "has": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-                    "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+                    "bundled": true,
                     "requires": {
                         "function-bind": "^1.1.1"
                     }
                 },
                 "has-flag": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-                    "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+                    "bundled": true
                 },
                 "has-symbols": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-                    "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
+                    "bundled": true
                 },
                 "has-unicode": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-                    "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+                    "bundled": true
                 },
                 "hosted-git-info": {
                     "version": "2.8.9",
-                    "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
-                    "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+                    "bundled": true
                 },
                 "http-cache-semantics": {
                     "version": "3.8.1",
-                    "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
-                    "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w=="
+                    "bundled": true
                 },
                 "http-proxy-agent": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
-                    "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
+                    "bundled": true,
                     "requires": {
                         "agent-base": "4",
                         "debug": "3.1.0"
@@ -14565,8 +14501,7 @@
                 },
                 "http-signature": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-                    "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0",
                         "jsprim": "^1.2.2",
@@ -14575,8 +14510,7 @@
                 },
                 "https-proxy-agent": {
                     "version": "2.2.4",
-                    "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
-                    "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+                    "bundled": true,
                     "requires": {
                         "agent-base": "^4.3.0",
                         "debug": "^3.1.0"
@@ -14584,52 +14518,44 @@
                 },
                 "humanize-ms": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
-                    "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+                    "bundled": true,
                     "requires": {
                         "ms": "^2.0.0"
                     }
                 },
                 "iconv-lite": {
                     "version": "0.4.23",
-                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-                    "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+                    "bundled": true,
                     "requires": {
                         "safer-buffer": ">= 2.1.2 < 3"
                     }
                 },
                 "iferr": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/iferr/-/iferr-1.0.2.tgz",
-                    "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg=="
+                    "bundled": true
                 },
                 "ignore-walk": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
-                    "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
+                    "bundled": true,
                     "requires": {
                         "minimatch": "^3.0.4"
                     }
                 },
                 "import-lazy": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
-                    "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
+                    "bundled": true
                 },
                 "imurmurhash": {
                     "version": "0.1.4",
-                    "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-                    "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+                    "bundled": true
                 },
                 "infer-owner": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
-                    "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="
+                    "bundled": true
                 },
                 "inflight": {
                     "version": "1.0.6",
-                    "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-                    "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+                    "bundled": true,
                     "requires": {
                         "once": "^1.3.0",
                         "wrappy": "1"
@@ -14637,18 +14563,15 @@
                 },
                 "inherits": {
                     "version": "2.0.4",
-                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-                    "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+                    "bundled": true
                 },
                 "ini": {
                     "version": "1.3.8",
-                    "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
-                    "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+                    "bundled": true
                 },
                 "init-package-json": {
                     "version": "1.10.3",
-                    "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz",
-                    "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==",
+                    "bundled": true,
                     "requires": {
                         "glob": "^7.1.1",
                         "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0",
@@ -14662,59 +14585,50 @@
                 },
                 "ip": {
                     "version": "1.1.5",
-                    "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
-                    "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+                    "bundled": true
                 },
                 "ip-regex": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
-                    "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
+                    "bundled": true
                 },
                 "is-callable": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
-                    "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="
+                    "bundled": true
                 },
                 "is-ci": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
-                    "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
+                    "bundled": true,
                     "requires": {
                         "ci-info": "^1.5.0"
                     },
                     "dependencies": {
                         "ci-info": {
                             "version": "1.6.0",
-                            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
-                            "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
+                            "bundled": true
                         }
                     }
                 },
                 "is-cidr": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.0.0.tgz",
-                    "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==",
+                    "version": "3.1.1",
+                    "bundled": true,
                     "requires": {
                         "cidr-regex": "^2.0.10"
                     }
                 },
                 "is-date-object": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
-                    "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
+                    "bundled": true
                 },
                 "is-fullwidth-code-point": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-                    "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+                    "bundled": true,
                     "requires": {
                         "number-is-nan": "^1.0.0"
                     }
                 },
                 "is-installed-globally": {
                     "version": "0.1.0",
-                    "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
-                    "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
+                    "bundled": true,
                     "requires": {
                         "global-dirs": "^0.1.0",
                         "is-path-inside": "^1.0.0"
@@ -14722,103 +14636,88 @@
                 },
                 "is-npm": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
-                    "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
+                    "bundled": true
                 },
                 "is-obj": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
-                    "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+                    "bundled": true
                 },
                 "is-path-inside": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
-                    "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+                    "bundled": true,
                     "requires": {
                         "path-is-inside": "^1.0.1"
                     }
                 },
                 "is-redirect": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
-                    "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
+                    "bundled": true
                 },
                 "is-regex": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
-                    "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+                    "bundled": true,
                     "requires": {
                         "has": "^1.0.1"
                     }
                 },
                 "is-retry-allowed": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
-                    "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
+                    "bundled": true
                 },
                 "is-stream": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-                    "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+                    "bundled": true
                 },
                 "is-symbol": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
-                    "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+                    "bundled": true,
                     "requires": {
                         "has-symbols": "^1.0.0"
                     }
                 },
                 "is-typedarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-                    "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+                    "bundled": true
                 },
                 "isarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-                    "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+                    "bundled": true
                 },
                 "isexe": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-                    "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+                    "bundled": true
                 },
                 "isstream": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-                    "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+                    "bundled": true
                 },
                 "jsbn": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-                    "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-                    "optional": true
+                    "bundled": true
                 },
                 "json-parse-better-errors": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-                    "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+                    "bundled": true
+                },
+                "json-parse-even-better-errors": {
+                    "version": "2.3.1",
+                    "bundled": true
                 },
                 "json-schema": {
                     "version": "0.4.0",
-                    "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
-                    "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
+                    "bundled": true
                 },
                 "json-stringify-safe": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-                    "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+                    "bundled": true
                 },
                 "jsonparse": {
                     "version": "1.3.1",
-                    "resolved": false,
-                    "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+                    "bundled": true
                 },
                 "jsprim": {
                     "version": "1.4.2",
-                    "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
-                    "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "1.0.0",
                         "extsprintf": "1.3.0",
@@ -14828,21 +14727,18 @@
                 },
                 "latest-version": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
-                    "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
+                    "bundled": true,
                     "requires": {
                         "package-json": "^4.0.0"
                     }
                 },
                 "lazy-property": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz",
-                    "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc="
+                    "bundled": true
                 },
                 "libcipm": {
                     "version": "4.0.8",
-                    "resolved": "https://registry.npmjs.org/libcipm/-/libcipm-4.0.8.tgz",
-                    "integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==",
+                    "bundled": true,
                     "requires": {
                         "bin-links": "^1.1.2",
                         "bluebird": "^3.5.1",
@@ -14863,8 +14759,7 @@
                 },
                 "libnpm": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/libnpm/-/libnpm-3.0.1.tgz",
-                    "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==",
+                    "bundled": true,
                     "requires": {
                         "bin-links": "^1.1.2",
                         "bluebird": "^3.5.3",
@@ -14890,8 +14785,7 @@
                 },
                 "libnpmaccess": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.2.tgz",
-                    "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "get-stream": "^4.0.0",
@@ -14901,8 +14795,7 @@
                 },
                 "libnpmconfig": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz",
-                    "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1",
                         "find-up": "^3.0.0",
@@ -14911,16 +14804,14 @@
                     "dependencies": {
                         "find-up": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-                            "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+                            "bundled": true,
                             "requires": {
                                 "locate-path": "^3.0.0"
                             }
                         },
                         "locate-path": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-                            "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+                            "bundled": true,
                             "requires": {
                                 "p-locate": "^3.0.0",
                                 "path-exists": "^3.0.0"
@@ -14928,31 +14819,27 @@
                         },
                         "p-limit": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
-                            "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+                            "bundled": true,
                             "requires": {
                                 "p-try": "^2.0.0"
                             }
                         },
                         "p-locate": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-                            "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+                            "bundled": true,
                             "requires": {
                                 "p-limit": "^2.0.0"
                             }
                         },
                         "p-try": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-                            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+                            "bundled": true
                         }
                     }
                 },
                 "libnpmhook": {
                     "version": "5.0.3",
-                    "resolved": "https://registry.npmjs.org/libnpmhook/-/libnpmhook-5.0.3.tgz",
-                    "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.4.1",
@@ -14962,8 +14849,7 @@
                 },
                 "libnpmorg": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/libnpmorg/-/libnpmorg-1.0.1.tgz",
-                    "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.4.1",
@@ -14973,8 +14859,7 @@
                 },
                 "libnpmpublish": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-1.1.2.tgz",
-                    "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.5.1",
@@ -14989,8 +14874,7 @@
                 },
                 "libnpmsearch": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-2.0.2.tgz",
-                    "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1",
                         "get-stream": "^4.0.0",
@@ -14999,8 +14883,7 @@
                 },
                 "libnpmteam": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmteam/-/libnpmteam-1.0.2.tgz",
-                    "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^2.0.0",
                         "figgy-pudding": "^3.4.1",
@@ -15010,8 +14893,7 @@
                 },
                 "libnpx": {
                     "version": "10.2.4",
-                    "resolved": "https://registry.npmjs.org/libnpx/-/libnpx-10.2.4.tgz",
-                    "integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==",
+                    "bundled": true,
                     "requires": {
                         "dotenv": "^5.0.1",
                         "npm-package-arg": "^6.0.0",
@@ -15024,31 +14906,28 @@
                     }
                 },
                 "lock-verify": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.1.0.tgz",
-                    "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==",
+                    "version": "2.2.2",
+                    "bundled": true,
                     "requires": {
+                        "@iarna/cli": "^2.1.0",
                         "npm-package-arg": "^6.1.0",
                         "semver": "^5.4.1"
                     }
                 },
                 "lockfile": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz",
-                    "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==",
+                    "bundled": true,
                     "requires": {
                         "signal-exit": "^3.0.2"
                     }
                 },
                 "lodash._baseindexof": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz",
-                    "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw="
+                    "bundled": true
                 },
                 "lodash._baseuniq": {
                     "version": "4.6.0",
-                    "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
-                    "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=",
+                    "bundled": true,
                     "requires": {
                         "lodash._createset": "~4.0.0",
                         "lodash._root": "~3.0.0"
@@ -15056,87 +14935,72 @@
                 },
                 "lodash._bindcallback": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
-                    "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4="
+                    "bundled": true
                 },
                 "lodash._cacheindexof": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz",
-                    "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI="
+                    "bundled": true
                 },
                 "lodash._createcache": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz",
-                    "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=",
+                    "bundled": true,
                     "requires": {
                         "lodash._getnative": "^3.0.0"
                     }
                 },
                 "lodash._createset": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
-                    "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY="
+                    "bundled": true
                 },
                 "lodash._getnative": {
                     "version": "3.9.1",
-                    "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-                    "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
+                    "bundled": true
                 },
                 "lodash._root": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
-                    "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI="
+                    "bundled": true
                 },
                 "lodash.clonedeep": {
                     "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
-                    "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
+                    "bundled": true
                 },
                 "lodash.restparam": {
                     "version": "3.6.1",
-                    "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
-                    "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU="
+                    "bundled": true
                 },
                 "lodash.union": {
                     "version": "4.6.0",
-                    "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
-                    "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
+                    "bundled": true
                 },
                 "lodash.uniq": {
                     "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-                    "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
+                    "bundled": true
                 },
                 "lodash.without": {
                     "version": "4.4.0",
-                    "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz",
-                    "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw="
+                    "bundled": true
                 },
                 "lowercase-keys": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
-                    "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
+                    "bundled": true
                 },
                 "lru-cache": {
                     "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-                    "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+                    "bundled": true,
                     "requires": {
                         "yallist": "^3.0.2"
                     }
                 },
                 "make-dir": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
-                    "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+                    "bundled": true,
                     "requires": {
                         "pify": "^3.0.0"
                     }
                 },
                 "make-fetch-happen": {
                     "version": "5.0.2",
-                    "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz",
-                    "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
+                    "bundled": true,
                     "requires": {
                         "agentkeepalive": "^3.4.1",
                         "cacache": "^12.0.0",
@@ -15152,48 +15016,41 @@
                     }
                 },
                 "meant": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.2.tgz",
-                    "integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg=="
+                    "version": "1.0.3",
+                    "bundled": true
                 },
                 "mime-db": {
                     "version": "1.35.0",
-                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
-                    "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="
+                    "bundled": true
                 },
                 "mime-types": {
                     "version": "2.1.19",
-                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
-                    "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
+                    "bundled": true,
                     "requires": {
                         "mime-db": "~1.35.0"
                     }
                 },
                 "minimatch": {
-                    "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-                    "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+                    "version": "3.1.2",
+                    "bundled": true,
                     "requires": {
                         "brace-expansion": "^1.1.7"
                     }
                 },
                 "minimist": {
                     "version": "1.2.6",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
-                    "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+                    "bundled": true
                 },
                 "minizlib": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
-                    "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+                    "bundled": true,
                     "requires": {
                         "minipass": "^2.9.0"
                     },
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -15203,8 +15060,7 @@
                 },
                 "mississippi": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
-                    "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+                    "bundled": true,
                     "requires": {
                         "concat-stream": "^1.5.0",
                         "duplexify": "^3.4.2",
@@ -15219,24 +15075,15 @@
                     }
                 },
                 "mkdirp": {
-                    "version": "0.5.5",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-                    "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+                    "version": "0.5.6",
+                    "bundled": true,
                     "requires": {
-                        "minimist": "^1.2.5"
-                    },
-                    "dependencies": {
-                        "minimist": {
-                            "version": "1.2.6",
-                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
-                            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
-                        }
+                        "minimist": "^1.2.6"
                     }
                 },
                 "move-concurrently": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
-                    "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.1",
                         "copy-concurrently": "^1.0.0",
@@ -15248,25 +15095,21 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         }
                     }
                 },
                 "ms": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
-                    "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+                    "bundled": true
                 },
                 "mute-stream": {
                     "version": "0.0.7",
-                    "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
-                    "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
+                    "bundled": true
                 },
                 "node-fetch-npm": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz",
-                    "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==",
+                    "bundled": true,
                     "requires": {
                         "encoding": "^0.1.11",
                         "json-parse-better-errors": "^1.0.0",
@@ -15274,9 +15117,8 @@
                     }
                 },
                 "node-gyp": {
-                    "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz",
-                    "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==",
+                    "version": "5.1.1",
+                    "bundled": true,
                     "requires": {
                         "env-paths": "^2.2.0",
                         "glob": "^7.1.4",
@@ -15293,8 +15135,7 @@
                 },
                 "nopt": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
-                    "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
+                    "bundled": true,
                     "requires": {
                         "abbrev": "1",
                         "osenv": "^0.1.4"
@@ -15302,8 +15143,7 @@
                 },
                 "normalize-package-data": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
-                    "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+                    "bundled": true,
                     "requires": {
                         "hosted-git-info": "^2.1.4",
                         "resolve": "^1.10.0",
@@ -15313,8 +15153,7 @@
                     "dependencies": {
                         "resolve": {
                             "version": "1.10.0",
-                            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
-                            "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+                            "bundled": true,
                             "requires": {
                                 "path-parse": "^1.0.6"
                             }
@@ -15323,8 +15162,7 @@
                 },
                 "npm-audit-report": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-1.3.3.tgz",
-                    "integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==",
+                    "bundled": true,
                     "requires": {
                         "cli-table3": "^0.5.0",
                         "console-control-strings": "^1.1.0"
@@ -15332,29 +15170,25 @@
                 },
                 "npm-bundled": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
-                    "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
+                    "bundled": true,
                     "requires": {
                         "npm-normalize-package-bin": "^1.0.1"
                     }
                 },
                 "npm-cache-filename": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz",
-                    "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE="
+                    "bundled": true
                 },
                 "npm-install-checks": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.2.tgz",
-                    "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==",
+                    "bundled": true,
                     "requires": {
                         "semver": "^2.3.0 || 3.x || 4 || 5"
                     }
                 },
                 "npm-lifecycle": {
                     "version": "3.1.5",
-                    "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz",
-                    "integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==",
+                    "bundled": true,
                     "requires": {
                         "byline": "^5.0.0",
                         "graceful-fs": "^4.1.15",
@@ -15368,18 +15202,15 @@
                 },
                 "npm-logical-tree": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz",
-                    "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg=="
+                    "bundled": true
                 },
                 "npm-normalize-package-bin": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
-                    "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
+                    "bundled": true
                 },
                 "npm-package-arg": {
                     "version": "6.1.1",
-                    "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
-                    "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
+                    "bundled": true,
                     "requires": {
                         "hosted-git-info": "^2.7.1",
                         "osenv": "^0.1.5",
@@ -15389,8 +15220,7 @@
                 },
                 "npm-packlist": {
                     "version": "1.4.8",
-                    "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
-                    "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
+                    "bundled": true,
                     "requires": {
                         "ignore-walk": "^3.0.1",
                         "npm-bundled": "^1.0.1",
@@ -15399,8 +15229,7 @@
                 },
                 "npm-pick-manifest": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz",
-                    "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1",
                         "npm-package-arg": "^6.0.0",
@@ -15409,8 +15238,7 @@
                 },
                 "npm-profile": {
                     "version": "4.0.4",
-                    "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz",
-                    "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.2 || 2",
                         "figgy-pudding": "^3.4.1",
@@ -15419,8 +15247,7 @@
                 },
                 "npm-registry-fetch": {
                     "version": "4.0.7",
-                    "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz",
-                    "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==",
+                    "bundled": true,
                     "requires": {
                         "JSONStream": "^1.3.4",
                         "bluebird": "^3.5.1",
@@ -15433,28 +15260,24 @@
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.1",
-                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-                            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+                            "bundled": true
                         }
                     }
                 },
                 "npm-run-path": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-                    "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+                    "bundled": true,
                     "requires": {
                         "path-key": "^2.0.0"
                     }
                 },
                 "npm-user-validate": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-1.0.1.tgz",
-                    "integrity": "sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw=="
+                    "bundled": true
                 },
                 "npmlog": {
                     "version": "4.1.2",
-                    "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-                    "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+                    "bundled": true,
                     "requires": {
                         "are-we-there-yet": "~1.1.2",
                         "console-control-strings": "~1.1.0",
@@ -15464,28 +15287,23 @@
                 },
                 "number-is-nan": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-                    "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+                    "bundled": true
                 },
                 "oauth-sign": {
                     "version": "0.9.0",
-                    "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-                    "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
+                    "bundled": true
                 },
                 "object-assign": {
                     "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-                    "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+                    "bundled": true
                 },
                 "object-keys": {
                     "version": "1.0.12",
-                    "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
-                    "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
+                    "bundled": true
                 },
                 "object.getownpropertydescriptors": {
                     "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
-                    "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
+                    "bundled": true,
                     "requires": {
                         "define-properties": "^1.1.2",
                         "es-abstract": "^1.5.1"
@@ -15493,31 +15311,26 @@
                 },
                 "once": {
                     "version": "1.4.0",
-                    "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-                    "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+                    "bundled": true,
                     "requires": {
                         "wrappy": "1"
                     }
                 },
                 "opener": {
                     "version": "1.5.2",
-                    "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
-                    "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="
+                    "bundled": true
                 },
                 "os-homedir": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-                    "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+                    "bundled": true
                 },
                 "os-tmpdir": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-                    "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+                    "bundled": true
                 },
                 "osenv": {
                     "version": "0.1.5",
-                    "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-                    "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+                    "bundled": true,
                     "requires": {
                         "os-homedir": "^1.0.0",
                         "os-tmpdir": "^1.0.0"
@@ -15525,13 +15338,11 @@
                 },
                 "p-finally": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-                    "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+                    "bundled": true
                 },
                 "package-json": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
-                    "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
+                    "bundled": true,
                     "requires": {
                         "got": "^6.7.1",
                         "registry-auth-token": "^3.0.1",
@@ -15541,8 +15352,7 @@
                 },
                 "pacote": {
                     "version": "9.5.12",
-                    "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz",
-                    "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==",
+                    "bundled": true,
                     "requires": {
                         "bluebird": "^3.5.3",
                         "cacache": "^12.0.2",
@@ -15578,8 +15388,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -15589,8 +15398,7 @@
                 },
                 "parallel-transform": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
-                    "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
+                    "bundled": true,
                     "requires": {
                         "cyclist": "~0.2.2",
                         "inherits": "^2.0.3",
@@ -15599,8 +15407,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -15609,72 +15416,72 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "path-exists": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-                    "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+                    "bundled": true
                 },
                 "path-is-absolute": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-                    "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+                    "bundled": true
                 },
                 "path-is-inside": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-                    "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
+                    "bundled": true
                 },
                 "path-key": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-                    "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+                    "bundled": true
                 },
                 "path-parse": {
                     "version": "1.0.7",
-                    "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-                    "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+                    "bundled": true
                 },
                 "performance-now": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-                    "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+                    "bundled": true
                 },
                 "pify": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+                    "bundled": true
                 },
                 "prepend-http": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-                    "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
+                    "bundled": true
                 },
                 "process-nextick-args": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-                    "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
+                    "bundled": true
                 },
                 "promise-inflight": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-                    "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
+                    "bundled": true
                 },
                 "promise-retry": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
-                    "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
+                    "bundled": true,
                     "requires": {
                         "err-code": "^1.0.0",
                         "retry": "^0.10.0"
@@ -15682,51 +15489,43 @@
                     "dependencies": {
                         "retry": {
                             "version": "0.10.1",
-                            "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
-                            "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
+                            "bundled": true
                         }
                     }
                 },
                 "promzard": {
                     "version": "0.3.0",
-                    "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz",
-                    "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=",
+                    "bundled": true,
                     "requires": {
                         "read": "1"
                     }
                 },
                 "proto-list": {
                     "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
-                    "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
+                    "bundled": true
                 },
                 "protoduck": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
-                    "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
+                    "bundled": true,
                     "requires": {
                         "genfun": "^5.0.0"
                     }
                 },
                 "prr": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-                    "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
+                    "bundled": true
                 },
                 "pseudomap": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-                    "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+                    "bundled": true
                 },
                 "psl": {
-                    "version": "1.1.29",
-                    "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
-                    "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
+                    "version": "1.9.0",
+                    "bundled": true
                 },
                 "pump": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
-                    "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+                    "bundled": true,
                     "requires": {
                         "end-of-stream": "^1.1.0",
                         "once": "^1.3.1"
@@ -15734,8 +15533,7 @@
                 },
                 "pumpify": {
                     "version": "1.5.1",
-                    "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
-                    "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+                    "bundled": true,
                     "requires": {
                         "duplexify": "^3.6.0",
                         "inherits": "^2.0.3",
@@ -15744,8 +15542,7 @@
                     "dependencies": {
                         "pump": {
                             "version": "2.0.1",
-                            "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
-                            "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+                            "bundled": true,
                             "requires": {
                                 "end-of-stream": "^1.1.0",
                                 "once": "^1.3.1"
@@ -15753,40 +15550,31 @@
                         }
                     }
                 },
-                "punycode": {
-                    "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-                    "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
-                },
                 "qrcode-terminal": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz",
-                    "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="
+                    "bundled": true
                 },
                 "qs": {
-                    "version": "6.5.2",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-                    "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+                    "version": "6.5.3",
+                    "bundled": true
                 },
                 "query-string": {
-                    "version": "6.8.2",
-                    "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.2.tgz",
-                    "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==",
+                    "version": "6.14.1",
+                    "bundled": true,
                     "requires": {
                         "decode-uri-component": "^0.2.0",
+                        "filter-obj": "^1.1.0",
                         "split-on-first": "^1.0.0",
                         "strict-uri-encode": "^2.0.0"
                     }
                 },
                 "qw": {
-                    "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.1.tgz",
-                    "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ="
+                    "version": "1.0.2",
+                    "bundled": true
                 },
                 "rc": {
                     "version": "1.2.8",
-                    "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
-                    "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+                    "bundled": true,
                     "requires": {
                         "deep-extend": "^0.6.0",
                         "ini": "~1.3.0",
@@ -15796,24 +15584,21 @@
                 },
                 "read": {
                     "version": "1.0.7",
-                    "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
-                    "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
+                    "bundled": true,
                     "requires": {
                         "mute-stream": "~0.0.4"
                     }
                 },
                 "read-cmd-shim": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz",
-                    "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2"
                     }
                 },
                 "read-installed": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz",
-                    "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=",
+                    "bundled": true,
                     "requires": {
                         "debuglog": "^1.0.1",
                         "graceful-fs": "^4.1.2",
@@ -15825,21 +15610,18 @@
                     }
                 },
                 "read-package-json": {
-                    "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz",
-                    "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
+                    "version": "2.1.2",
+                    "bundled": true,
                     "requires": {
                         "glob": "^7.1.1",
-                        "graceful-fs": "^4.1.2",
-                        "json-parse-better-errors": "^1.0.1",
+                        "json-parse-even-better-errors": "^2.3.0",
                         "normalize-package-data": "^2.0.0",
                         "npm-normalize-package-bin": "^1.0.0"
                     }
                 },
                 "read-package-tree": {
                     "version": "5.3.1",
-                    "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz",
-                    "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
+                    "bundled": true,
                     "requires": {
                         "read-package-json": "^2.0.0",
                         "readdir-scoped-modules": "^1.0.0",
@@ -15848,8 +15630,7 @@
                 },
                 "readable-stream": {
                     "version": "3.6.0",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
-                    "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+                    "bundled": true,
                     "requires": {
                         "inherits": "^2.0.3",
                         "string_decoder": "^1.1.1",
@@ -15858,8 +15639,7 @@
                 },
                 "readdir-scoped-modules": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
-                    "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
+                    "bundled": true,
                     "requires": {
                         "debuglog": "^1.0.1",
                         "dezalgo": "^1.0.0",
@@ -15869,8 +15649,7 @@
                 },
                 "registry-auth-token": {
                     "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
-                    "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
+                    "bundled": true,
                     "requires": {
                         "rc": "^1.1.6",
                         "safe-buffer": "^5.0.1"
@@ -15878,16 +15657,14 @@
                 },
                 "registry-url": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
-                    "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+                    "bundled": true,
                     "requires": {
                         "rc": "^1.0.1"
                     }
                 },
                 "request": {
-                    "version": "2.88.0",
-                    "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-                    "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+                    "version": "2.88.2",
+                    "bundled": true,
                     "requires": {
                         "aws-sign2": "~0.7.0",
                         "aws4": "^1.8.0",
@@ -15896,7 +15673,7 @@
                         "extend": "~3.0.2",
                         "forever-agent": "~0.6.1",
                         "form-data": "~2.3.2",
-                        "har-validator": "~5.1.0",
+                        "har-validator": "~5.1.3",
                         "http-signature": "~1.2.0",
                         "is-typedarray": "~1.0.0",
                         "isstream": "~0.1.2",
@@ -15906,122 +15683,103 @@
                         "performance-now": "^2.1.0",
                         "qs": "~6.5.2",
                         "safe-buffer": "^5.1.2",
-                        "tough-cookie": "~2.4.3",
+                        "tough-cookie": "~2.5.0",
                         "tunnel-agent": "^0.6.0",
                         "uuid": "^3.3.2"
                     }
                 },
                 "require-directory": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-                    "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+                    "bundled": true
                 },
                 "require-main-filename": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-                    "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+                    "bundled": true
                 },
                 "resolve-from": {
                     "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-                    "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
+                    "bundled": true
                 },
                 "retry": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
-                    "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
+                    "bundled": true
                 },
                 "rimraf": {
                     "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+                    "bundled": true,
                     "requires": {
                         "glob": "^7.1.3"
                     }
                 },
                 "run-queue": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
-                    "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+                    "bundled": true,
                     "requires": {
                         "aproba": "^1.1.1"
                     },
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                            "bundled": true
                         }
                     }
                 },
                 "safe-buffer": {
-                    "version": "5.1.2",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-                    "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+                    "version": "5.2.1",
+                    "bundled": true
                 },
                 "safer-buffer": {
                     "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-                    "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+                    "bundled": true
                 },
                 "semver": {
                     "version": "5.7.1",
-                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+                    "bundled": true
                 },
                 "semver-diff": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
-                    "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+                    "bundled": true,
                     "requires": {
                         "semver": "^5.0.3"
                     }
                 },
                 "set-blocking": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-                    "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+                    "bundled": true
                 },
                 "sha": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/sha/-/sha-3.0.0.tgz",
-                    "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.2"
                     }
                 },
                 "shebang-command": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-                    "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+                    "bundled": true,
                     "requires": {
                         "shebang-regex": "^1.0.0"
                     }
                 },
                 "shebang-regex": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-                    "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+                    "bundled": true
                 },
                 "signal-exit": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-                    "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+                    "bundled": true
                 },
                 "slide": {
                     "version": "1.1.6",
-                    "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
-                    "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
+                    "bundled": true
                 },
                 "smart-buffer": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
-                    "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw=="
+                    "bundled": true
                 },
                 "socks": {
                     "version": "2.3.3",
-                    "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
-                    "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
+                    "bundled": true,
                     "requires": {
                         "ip": "1.1.5",
                         "smart-buffer": "^4.1.0"
@@ -16029,8 +15787,7 @@
                 },
                 "socks-proxy-agent": {
                     "version": "4.0.2",
-                    "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz",
-                    "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
+                    "bundled": true,
                     "requires": {
                         "agent-base": "~4.2.1",
                         "socks": "~2.3.2"
@@ -16038,8 +15795,7 @@
                     "dependencies": {
                         "agent-base": {
                             "version": "4.2.1",
-                            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
-                            "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+                            "bundled": true,
                             "requires": {
                                 "es6-promisify": "^5.0.0"
                             }
@@ -16048,13 +15804,11 @@
                 },
                 "sorted-object": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz",
-                    "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw="
+                    "bundled": true
                 },
                 "sorted-union-stream": {
                     "version": "2.1.3",
-                    "resolved": "https://registry.npmjs.org/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz",
-                    "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=",
+                    "bundled": true,
                     "requires": {
                         "from2": "^1.3.0",
                         "stream-iterate": "^1.1.0"
@@ -16062,8 +15816,7 @@
                     "dependencies": {
                         "from2": {
                             "version": "1.3.0",
-                            "resolved": "https://registry.npmjs.org/from2/-/from2-1.3.0.tgz",
-                            "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=",
+                            "bundled": true,
                             "requires": {
                                 "inherits": "~2.0.1",
                                 "readable-stream": "~1.1.10"
@@ -16071,13 +15824,11 @@
                         },
                         "isarray": {
                             "version": "0.0.1",
-                            "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-                            "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+                            "bundled": true
                         },
                         "readable-stream": {
                             "version": "1.1.14",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-                            "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.1",
@@ -16087,15 +15838,13 @@
                         },
                         "string_decoder": {
                             "version": "0.10.31",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-                            "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+                            "bundled": true
                         }
                     }
                 },
                 "spdx-correct": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
-                    "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
+                    "bundled": true,
                     "requires": {
                         "spdx-expression-parse": "^3.0.0",
                         "spdx-license-ids": "^3.0.0"
@@ -16103,13 +15852,11 @@
                 },
                 "spdx-exceptions": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
-                    "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
+                    "bundled": true
                 },
                 "spdx-expression-parse": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-                    "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+                    "bundled": true,
                     "requires": {
                         "spdx-exceptions": "^2.1.0",
                         "spdx-license-ids": "^3.0.0"
@@ -16117,18 +15864,15 @@
                 },
                 "spdx-license-ids": {
                     "version": "3.0.5",
-                    "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
-                    "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
+                    "bundled": true
                 },
                 "split-on-first": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
-                    "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+                    "bundled": true
                 },
                 "sshpk": {
-                    "version": "1.14.2",
-                    "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
-                    "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
+                    "version": "1.17.0",
+                    "bundled": true,
                     "requires": {
                         "asn1": "~0.2.3",
                         "assert-plus": "^1.0.0",
@@ -16143,16 +15887,14 @@
                 },
                 "ssri": {
                     "version": "6.0.2",
-                    "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
-                    "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+                    "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1"
                     }
                 },
                 "stream-each": {
                     "version": "1.2.2",
-                    "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz",
-                    "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==",
+                    "bundled": true,
                     "requires": {
                         "end-of-stream": "^1.1.0",
                         "stream-shift": "^1.0.0"
@@ -16160,8 +15902,7 @@
                 },
                 "stream-iterate": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/stream-iterate/-/stream-iterate-1.2.0.tgz",
-                    "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=",
+                    "bundled": true,
                     "requires": {
                         "readable-stream": "^2.1.5",
                         "stream-shift": "^1.0.0"
@@ -16169,8 +15910,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -16179,32 +15919,40 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "stream-shift": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
-                    "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
+                    "bundled": true
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-                    "resolved": false,
-                    "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
+                    "bundled": true
                 },
                 "string-width": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-                    "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+                    "bundled": true,
                     "requires": {
                         "is-fullwidth-code-point": "^2.0.0",
                         "strip-ansi": "^4.0.0"
@@ -16212,18 +15960,15 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-                            "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+                            "bundled": true
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "strip-ansi": {
                             "version": "4.0.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-                            "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^3.0.0"
                             }
@@ -16245,39 +15990,33 @@
                 },
                 "stringify-package": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz",
-                    "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg=="
+                    "bundled": true
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+                    "bundled": true,
                     "requires": {
                         "ansi-regex": "^2.0.0"
                     }
                 },
                 "strip-eof": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-                    "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+                    "bundled": true
                 },
                 "strip-json-comments": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-                    "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+                    "bundled": true
                 },
                 "supports-color": {
                     "version": "5.4.0",
-                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-                    "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+                    "bundled": true,
                     "requires": {
                         "has-flag": "^3.0.0"
                     }
                 },
                 "tar": {
                     "version": "4.4.19",
-                    "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
-                    "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
+                    "bundled": true,
                     "requires": {
                         "chownr": "^1.1.4",
                         "fs-minipass": "^1.2.7",
@@ -16290,8 +16029,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-                            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
@@ -16299,38 +16037,32 @@
                         },
                         "safe-buffer": {
                             "version": "5.2.1",
-                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-                            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+                            "bundled": true
                         },
                         "yallist": {
                             "version": "3.1.1",
-                            "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-                            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+                            "bundled": true
                         }
                     }
                 },
                 "term-size": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
-                    "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+                    "bundled": true,
                     "requires": {
                         "execa": "^0.7.0"
                     }
                 },
                 "text-table": {
                     "version": "0.2.0",
-                    "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-                    "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
+                    "bundled": true
                 },
                 "through": {
                     "version": "2.3.8",
-                    "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-                    "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+                    "bundled": true
                 },
                 "through2": {
                     "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
-                    "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+                    "bundled": true,
                     "requires": {
                         "readable-stream": "^2.1.5",
                         "xtend": "~4.0.1"
@@ -16338,8 +16070,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-                            "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+                            "bundled": true,
                             "requires": {
                                 "core-util-is": "~1.0.0",
                                 "inherits": "~2.0.3",
@@ -16348,104 +16079,106 @@
                                 "safe-buffer": "~5.1.1",
                                 "string_decoder": "~1.1.1",
                                 "util-deprecate": "~1.0.1"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-                            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+                            "bundled": true,
                             "requires": {
                                 "safe-buffer": "~5.1.0"
+                            },
+                            "dependencies": {
+                                "safe-buffer": {
+                                    "version": "5.1.2",
+                                    "bundled": true
+                                }
                             }
                         }
                     }
                 },
                 "timed-out": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
-                    "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
+                    "bundled": true
                 },
                 "tiny-relative-date": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz",
-                    "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A=="
+                    "bundled": true
                 },
                 "tough-cookie": {
-                    "version": "2.4.3",
-                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-                    "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+                    "version": "2.5.0",
+                    "bundled": true,
                     "requires": {
-                        "psl": "^1.1.24",
-                        "punycode": "^1.4.1"
+                        "psl": "^1.1.28",
+                        "punycode": "^2.1.1"
+                    },
+                    "dependencies": {
+                        "punycode": {
+                            "version": "2.1.1",
+                            "bundled": true
+                        }
                     }
                 },
                 "tunnel-agent": {
                     "version": "0.6.0",
-                    "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-                    "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+                    "bundled": true,
                     "requires": {
                         "safe-buffer": "^5.0.1"
                     }
                 },
                 "tweetnacl": {
                     "version": "0.14.5",
-                    "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-                    "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
-                    "optional": true
+                    "bundled": true
                 },
                 "typedarray": {
                     "version": "0.0.6",
-                    "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-                    "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+                    "bundled": true
                 },
                 "uid-number": {
                     "version": "0.0.6",
-                    "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
-                    "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE="
+                    "bundled": true
                 },
                 "umask": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz",
-                    "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0="
+                    "bundled": true
                 },
                 "unique-filename": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
-                    "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+                    "bundled": true,
                     "requires": {
                         "unique-slug": "^2.0.0"
                     }
                 },
                 "unique-slug": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
-                    "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
+                    "bundled": true,
                     "requires": {
                         "imurmurhash": "^0.1.4"
                     }
                 },
                 "unique-string": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
-                    "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+                    "bundled": true,
                     "requires": {
                         "crypto-random-string": "^1.0.0"
                     }
                 },
                 "unpipe": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-                    "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+                    "bundled": true
                 },
                 "unzip-response": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
-                    "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
+                    "bundled": true
                 },
                 "update-notifier": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
-                    "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
+                    "bundled": true,
                     "requires": {
                         "boxen": "^1.2.1",
                         "chalk": "^2.0.1",
@@ -16460,55 +16193,47 @@
                     }
                 },
                 "uri-js": {
-                    "version": "4.4.0",
-                    "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
-                    "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+                    "version": "4.4.1",
+                    "bundled": true,
                     "requires": {
                         "punycode": "^2.1.0"
                     },
                     "dependencies": {
                         "punycode": {
                             "version": "2.1.1",
-                            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-                            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+                            "bundled": true
                         }
                     }
                 },
                 "url-parse-lax": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
-                    "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+                    "bundled": true,
                     "requires": {
                         "prepend-http": "^1.0.1"
                     }
                 },
                 "util-deprecate": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-                    "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+                    "bundled": true
                 },
                 "util-extend": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
-                    "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8="
+                    "bundled": true
                 },
                 "util-promisify": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz",
-                    "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
+                    "bundled": true,
                     "requires": {
                         "object.getownpropertydescriptors": "^2.0.3"
                     }
                 },
                 "uuid": {
-                    "version": "3.3.3",
-                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
-                    "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
+                    "version": "3.4.0",
+                    "bundled": true
                 },
                 "validate-npm-package-license": {
                     "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-                    "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+                    "bundled": true,
                     "requires": {
                         "spdx-correct": "^3.0.0",
                         "spdx-expression-parse": "^3.0.0"
@@ -16516,16 +16241,14 @@
                 },
                 "validate-npm-package-name": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
-                    "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
+                    "bundled": true,
                     "requires": {
                         "builtins": "^1.0.3"
                     }
                 },
                 "verror": {
                     "version": "1.10.0",
-                    "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-                    "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+                    "bundled": true,
                     "requires": {
                         "assert-plus": "^1.0.0",
                         "core-util-is": "1.0.2",
@@ -16534,37 +16257,32 @@
                 },
                 "wcwidth": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
-                    "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
+                    "bundled": true,
                     "requires": {
                         "defaults": "^1.0.3"
                     }
                 },
                 "which": {
                     "version": "1.3.1",
-                    "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
-                    "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+                    "bundled": true,
                     "requires": {
                         "isexe": "^2.0.0"
                     }
                 },
                 "which-module": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-                    "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+                    "bundled": true
                 },
                 "wide-align": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
-                    "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^1.0.2"
                     },
                     "dependencies": {
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                            "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                            "bundled": true,
                             "requires": {
                                 "code-point-at": "^1.0.0",
                                 "is-fullwidth-code-point": "^1.0.0",
@@ -16575,24 +16293,21 @@
                 },
                 "widest-line": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
-                    "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
+                    "bundled": true,
                     "requires": {
                         "string-width": "^2.1.1"
                     }
                 },
                 "worker-farm": {
                     "version": "1.7.0",
-                    "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
-                    "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+                    "bundled": true,
                     "requires": {
                         "errno": "~0.1.7"
                     }
                 },
                 "wrap-ansi": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-                    "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+                    "bundled": true,
                     "requires": {
                         "ansi-styles": "^3.2.0",
                         "string-width": "^3.0.0",
@@ -16601,18 +16316,15 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "4.1.1",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                            "bundled": true
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "3.1.0",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-                            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                            "bundled": true,
                             "requires": {
                                 "emoji-regex": "^7.0.1",
                                 "is-fullwidth-code-point": "^2.0.0",
@@ -16621,8 +16333,7 @@
                         },
                         "strip-ansi": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-                            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^4.1.0"
                             }
@@ -16631,13 +16342,11 @@
                 },
                 "wrappy": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-                    "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+                    "bundled": true
                 },
                 "write-file-atomic": {
                     "version": "2.4.3",
-                    "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
-                    "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
+                    "bundled": true,
                     "requires": {
                         "graceful-fs": "^4.1.11",
                         "imurmurhash": "^0.1.4",
@@ -16646,28 +16355,23 @@
                 },
                 "xdg-basedir": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
-                    "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
+                    "bundled": true
                 },
                 "xtend": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
-                    "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+                    "bundled": true
                 },
                 "y18n": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
-                    "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ=="
+                    "bundled": true
                 },
                 "yallist": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
-                    "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
+                    "bundled": true
                 },
                 "yargs": {
                     "version": "14.2.3",
-                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
-                    "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+                    "bundled": true,
                     "requires": {
                         "cliui": "^5.0.0",
                         "decamelize": "^1.2.0",
@@ -16684,26 +16388,22 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "4.1.0",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+                            "bundled": true
                         },
                         "find-up": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-                            "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+                            "bundled": true,
                             "requires": {
                                 "locate-path": "^3.0.0"
                             }
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-                            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+                            "bundled": true
                         },
                         "locate-path": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-                            "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+                            "bundled": true,
                             "requires": {
                                 "p-locate": "^3.0.0",
                                 "path-exists": "^3.0.0"
@@ -16711,29 +16411,25 @@
                         },
                         "p-limit": {
                             "version": "2.3.0",
-                            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-                            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+                            "bundled": true,
                             "requires": {
                                 "p-try": "^2.0.0"
                             }
                         },
                         "p-locate": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-                            "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+                            "bundled": true,
                             "requires": {
                                 "p-limit": "^2.0.0"
                             }
                         },
                         "p-try": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-                            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+                            "bundled": true
                         },
                         "string-width": {
                             "version": "3.1.0",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-                            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                            "bundled": true,
                             "requires": {
                                 "emoji-regex": "^7.0.1",
                                 "is-fullwidth-code-point": "^2.0.0",
@@ -16742,8 +16438,7 @@
                         },
                         "strip-ansi": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-                            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                            "bundled": true,
                             "requires": {
                                 "ansi-regex": "^4.1.0"
                             }
@@ -16752,8 +16447,7 @@
                 },
                 "yargs-parser": {
                     "version": "15.0.1",
-                    "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
-                    "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+                    "bundled": true,
                     "requires": {
                         "camelcase": "^5.0.0",
                         "decamelize": "^1.2.0"
@@ -16761,8 +16455,7 @@
                     "dependencies": {
                         "camelcase": {
                             "version": "5.3.1",
-                            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-                            "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+                            "bundled": true
                         }
                     }
                 }
@@ -20314,7 +20007,7 @@
                 "component-emitter": {
                     "version": "1.2.1",
                     "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-                    "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA=="
+                    "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
                 },
                 "debug": {
                     "version": "4.1.1",
@@ -20327,7 +20020,7 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
                 }
             }
         },
@@ -21192,7 +20885,7 @@
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+            "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
         },
         "to-fast-properties": {
             "version": "2.0.0",
@@ -21563,7 +21256,7 @@
                 "strip-bom": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-                    "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+                    "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
                     "dev": true
                 }
             }
@@ -22770,7 +22463,7 @@
                 "resolve-cwd": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-                    "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
+                    "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
                     "dev": true,
                     "requires": {
                         "resolve-from": "^3.0.0"
@@ -22779,7 +22472,7 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
                     "dev": true
                 },
                 "semver": {
@@ -23320,7 +23013,7 @@
         "yeast": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+            "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
         },
         "yn": {
             "version": "3.1.1",
diff --git a/package.json b/package.json
index 9bfcf0cc8..712354355 100644
--- a/package.json
+++ b/package.json
@@ -237,7 +237,7 @@
         "nodemailer": "^5.1.1",
         "nodemon": "^1.19.4",
         "normalize.css": "^8.0.1",
-        "npm": "^6.14.16",
+        "npm": "^6.14.18",
         "p-limit": "^2.2.0",
         "passport": "^0.4.0",
         "passport-google-oauth20": "^2.0.0",
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 70fe7f2c0..aa09fb005 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -5,7 +5,7 @@ import { listSpec } from '../../fields/Schema';
 import { Cast, DocCast, StrCast } from '../../fields/Types';
 import { AudioField } from '../../fields/URLField';
 import { returnFalse } from '../../Utils';
-import { DocumentType } from '../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
 import { CollectionView } from '../views/collections/CollectionView';
@@ -32,11 +32,15 @@ export class DocumentManager {
     private constructor() {}
 
     private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = [];
-    public AddViewRenderedCb = (doc: Doc, func: (dv: DocumentView) => any) => {
-        const dv = this.getDocumentViewById(doc[Id]);
-        this._viewRenderedCbs.push({ doc, func });
-        if (dv) {
-            this.callAddViewFuncs(dv);
+    public AddViewRenderedCb = (doc: Opt<Doc>, func: (dv: DocumentView) => any) => {
+        if (doc) {
+            const dv = this.getDocumentViewById(doc[Id]);
+            this._viewRenderedCbs.push({ doc, func });
+            if (dv) {
+                this.callAddViewFuncs(dv);
+            }
+        } else {
+            func(undefined as any);
         }
     };
     callAddViewFuncs = (view: DocumentView) => {
@@ -190,6 +194,16 @@ export class DocumentManager {
         return toReturn;
     }
 
+    static GetContextPath(doc: Opt<Doc>) {
+        if (!doc) return [];
+        const srcContext = Cast(doc.context, Doc, null) ?? Cast(Cast(doc.annotationOn, Doc, null)?.context, Doc, null);
+        var containerDocContext = srcContext ? [srcContext] : [];
+        while (containerDocContext.length && containerDocContext[0]?.context && DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking && !DocumentManager.Instance.getDocumentView(containerDocContext[0])) {
+            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+        }
+        return containerDocContext;
+    }
+
     static playAudioAnno(doc: Doc) {
         const anno = Cast(doc[Doc.LayoutFieldKey(doc) + '-audioAnnotations'], listSpec(AudioField), null)?.lastElement();
         if (anno) {
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 0f1fc6b69..de1207ce4 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,8 +1,7 @@
-import React = require("react");
-import { observable, action, runInAction } from "mobx";
-import "./AntimodeMenu.scss";
-export interface AntimodeMenuProps {
-}
+import React = require('react');
+import { observable, action, runInAction } from 'mobx';
+import './AntimodeMenu.scss';
+export interface AntimodeMenuProps {}
 
 /**
  * This is an abstract class that serves as the base for a PDF-style or Marquee-style
@@ -17,15 +16,19 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
     @observable protected _top: number = -300;
     @observable protected _left: number = -300;
     @observable protected _opacity: number = 0;
-    @observable protected _transitionProperty: string = "opacity";
-    @observable protected _transitionDuration: string = "0.5s";
-    @observable protected _transitionDelay: string = "";
+    @observable protected _transitionProperty: string = 'opacity';
+    @observable protected _transitionDuration: string = '0.5s';
+    @observable protected _transitionDelay: string = '';
     @observable protected _canFade: boolean = false;
 
     @observable public Pinned: boolean = false;
 
-    get width() { return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().width : 0; }
-    get height() { return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().height : 0; }
+    get width() {
+        return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().width : 0;
+    }
+    get height() {
+        return this._mainCont.current ? this._mainCont.current.getBoundingClientRect().height : 0;
+    }
 
     @action
     /**
@@ -36,12 +39,12 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
      */
     public jumpTo = (x: number, y: number, forceJump: boolean = false) => {
         if (!this.Pinned || forceJump) {
-            this._transitionProperty = this._transitionDuration = this._transitionDelay = "";
+            this._transitionProperty = this._transitionDuration = this._transitionDelay = '';
             this._opacity = 1;
             this._left = x;
             this._top = y;
         }
-    }
+    };
 
     @action
     /**
@@ -51,56 +54,56 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
     public fadeOut = (forceOut: boolean) => {
         if (!this.Pinned) {
             if (this._opacity === 0.2) {
-                this._transitionProperty = "opacity";
-                this._transitionDuration = "0.1s";
+                this._transitionProperty = 'opacity';
+                this._transitionDuration = '0.1s';
             }
 
             if (forceOut) {
-                this._transitionProperty = "";
-                this._transitionDuration = "";
+                this._transitionProperty = '';
+                this._transitionDuration = '';
             }
-            this._transitionDelay = "";
+            this._transitionDelay = '';
             this._opacity = 0;
             this._left = this._top = -300;
         }
-    }
+    };
 
     @action
     protected pointerLeave = (e: React.PointerEvent) => {
         if (!this.Pinned && this._canFade) {
-            this._transitionProperty = "opacity";
-            this._transitionDuration = "0.5s";
-            this._transitionDelay = "1s";
+            this._transitionProperty = 'opacity';
+            this._transitionDuration = '0.5s';
+            this._transitionDelay = '1s';
             this._opacity = 0.2;
             setTimeout(() => this.fadeOut(false), 3000);
         }
-    }
+    };
 
     @action
     protected pointerEntered = (e: React.PointerEvent) => {
-        this._transitionProperty = "opacity";
-        this._transitionDuration = "0.1s";
-        this._transitionDelay = "";
+        this._transitionProperty = 'opacity';
+        this._transitionDuration = '0.1s';
+        this._transitionDelay = '';
         this._opacity = 1;
-    }
+    };
 
     @action
     protected togglePin = (e: React.MouseEvent) => {
-        runInAction(() => this.Pinned = !this.Pinned);
-    }
+        runInAction(() => (this.Pinned = !this.Pinned));
+    };
 
     protected dragStart = (e: React.PointerEvent) => {
-        document.removeEventListener("pointermove", this.dragging);
-        document.addEventListener("pointermove", this.dragging);
-        document.removeEventListener("pointerup", this.dragEnd);
-        document.addEventListener("pointerup", this.dragEnd);
+        document.removeEventListener('pointermove', this.dragging);
+        document.addEventListener('pointermove', this.dragging);
+        document.removeEventListener('pointerup', this.dragEnd);
+        document.addEventListener('pointerup', this.dragEnd);
 
         this._offsetX = e.pageX - this._mainCont.current!.getBoundingClientRect().left;
         this._offsetY = e.pageY - this._mainCont.current!.getBoundingClientRect().top;
 
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     @action
     protected dragging = (e: PointerEvent) => {
@@ -115,32 +118,41 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
 
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     protected dragEnd = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.dragging);
-        document.removeEventListener("pointerup", this.dragEnd);
+        document.removeEventListener('pointermove', this.dragging);
+        document.removeEventListener('pointerup', this.dragEnd);
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     protected handleContextMenu = (e: React.MouseEvent) => {
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     protected getDragger = () => {
-        return <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />;
-    }
+        return <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} />;
+    };
 
-    protected getElement(buttons: JSX.Element[]) {
+    protected getElement(buttons: JSX.Element) {
         return (
-            <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+            <div
+                className="antimodeMenu-cont"
+                onPointerLeave={this.pointerLeave}
+                onPointerEnter={this.pointerEntered}
+                ref={this._mainCont}
+                onContextMenu={this.handleContextMenu}
                 style={{
-                    left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay,
-                    position: this.Pinned ? "unset" : undefined
+                    left: this._left,
+                    top: this._top,
+                    opacity: this._opacity,
+                    transitionProperty: this._transitionProperty,
+                    transitionDuration: this._transitionDuration,
+                    transitionDelay: this._transitionDelay,
+                    position: this.Pinned ? 'unset' : undefined,
                 }}>
-                {/* {this.getDragger} */}
                 {buttons}
             </div>
         );
@@ -148,34 +160,51 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
 
     protected getElementVert(buttons: JSX.Element[]) {
         return (
-            <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+            <div
+                className="antimodeMenu-cont"
+                onPointerLeave={this.pointerLeave}
+                onPointerEnter={this.pointerEntered}
+                ref={this._mainCont}
+                onContextMenu={this.handleContextMenu}
                 style={{
                     left: this.Pinned ? undefined : this._left,
                     top: this.Pinned ? 0 : this._top,
                     right: this.Pinned ? 0 : undefined,
-                    height: "inherit",
+                    height: 'inherit',
                     width: 200,
-                    opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay,
-                    position: this.Pinned ? "absolute" : undefined
+                    opacity: this._opacity,
+                    transitionProperty: this._transitionProperty,
+                    transitionDuration: this._transitionDuration,
+                    transitionDelay: this._transitionDelay,
+                    position: this.Pinned ? 'absolute' : undefined,
                 }}>
                 {buttons}
             </div>
         );
     }
 
-
-
     protected getElementWithRows(rows: JSX.Element[], numRows: number, hasDragger: boolean = true) {
         return (
-            <div className="antimodeMenu-cont with-rows" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+            <div
+                className="antimodeMenu-cont with-rows"
+                onPointerLeave={this.pointerLeave}
+                onPointerEnter={this.pointerEntered}
+                ref={this._mainCont}
+                onContextMenu={this.handleContextMenu}
                 style={{
-                    left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty,
-                    transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, height: "auto",
-                    flexDirection: this.Pinned ? "row" : undefined, position: this.Pinned ? "unset" : undefined
+                    left: this._left,
+                    top: this._top,
+                    opacity: this._opacity,
+                    transitionProperty: this._transitionProperty,
+                    transitionDuration: this._transitionDuration,
+                    transitionDelay: this._transitionDelay,
+                    height: 'auto',
+                    flexDirection: this.Pinned ? 'row' : undefined,
+                    position: this.Pinned ? 'unset' : undefined,
                 }}>
-                {hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} /> : (null)}
+                {hasDragger ? <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} /> : null}
                 {rows}
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index f06dc93e3..f61d147cf 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -5,11 +5,11 @@ import { action, computed, observable, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast } from '../../fields/Types';
+import { Cast, DocCast, NumCast } from '../../fields/Types';
 import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
-import { Docs } from '../documents/Documents';
+import { Docs, DocUtils } from '../documents/Documents';
 import { DragManager } from '../util/DragManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SharingManager } from '../util/SharingManager';
@@ -21,12 +21,13 @@ import { Colors } from './global/globalEnums';
 import { LinkPopup } from './linking/LinkPopup';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewInternal, OpenWhereMod } from './nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
 import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
 import { TemplateMenu } from './TemplateMenu';
 import React = require('react');
 import { DocumentType } from '../documents/DocumentTypes';
+import { FontIconBox } from './nodes/button/FontIconBox';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -270,6 +271,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             </button>
                         </div>
                     </Tooltip>
+                    <Tooltip title={<div>open linked trail</div>}>
+                        <div className="documentButtonBar-button">
+                            <button style={{ backgroundColor: 'transparent', width: 35, height: 35, display: 'flex', justifyContent: 'center', alignItems: 'center', position: 'relative' }} onPointerDown={this.toggleTrail}>
+                                <FontAwesomeIcon icon="taxi" size="lg" />
+                            </button>
+                        </div>
+                    </Tooltip>
                 </div>
                 <div style={{ width: 25, height: 25 }}>
                     <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
@@ -483,6 +491,22 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         this._showLinkPopup = !this._showLinkPopup;
         e.stopPropagation();
     };
+    @action
+    toggleTrail = (e: React.PointerEvent) => {
+        const rootView = this.props.views()[0];
+        const rootDoc = rootView?.rootDoc;
+        if (rootDoc) {
+            const anchor = rootView.ComponentView?.getAnchor?.(true) ?? rootDoc;
+            const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
+            if (trail !== anchor.presTrail) {
+                DocUtils.MakeLink({ doc: anchor }, { doc: trail }, 'link trail');
+                anchor.presTrail = trail;
+            }
+            Doc.ActivePresentation = trail;
+            this.props.views().lastElement()?.props.addDocTab(trail, OpenWhere.replaceRight);
+        }
+        e.stopPropagation();
+    };
     render() {
         if (!this.view0) return null;
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4494166f2..839db5795 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -249,6 +249,7 @@ export class MainView extends React.Component {
                 fa.faTrash,
                 fa.faTrashAlt,
                 fa.faShare,
+                fa.faTaxi,
                 fa.faDownload,
                 fa.faExpandArrowsAlt,
                 fa.faLayerGroup,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 488f51d77..9581563ce 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -26,33 +26,39 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     render() {
         const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 19, transform: 'translate(-2px, -2px)' }} />;
-        const buttons = [
-            <Tooltip key="collect" title={<div className="dash-tooltip">Create a Collection</div>} placement="bottom">
-                <button className="antimodeMenu-button" onPointerDown={this.createCollection}>
-                    <FontAwesomeIcon icon="object-group" size="lg" />
-                </button>
-            </Tooltip>,
-            <Tooltip key="group" title={<div className="dash-tooltip">Create a Grouping</div>} placement="bottom">
-                <button className="antimodeMenu-button" onPointerDown={e => this.createCollection(e, true)}>
-                    <FontAwesomeIcon icon="layer-group" size="lg" />
-                </button>
-            </Tooltip>,
-            <Tooltip key="summarize" title={<div className="dash-tooltip">Summarize Documents</div>} placement="bottom">
-                <button className="antimodeMenu-button" onPointerDown={this.summarize}>
-                    <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
-                </button>
-            </Tooltip>,
-            <Tooltip key="delete" title={<div className="dash-tooltip">Delete Documents</div>} placement="bottom">
-                <button className="antimodeMenu-button" onPointerDown={this.delete}>
-                    <FontAwesomeIcon icon="trash-alt" size="lg" />
-                </button>
-            </Tooltip>,
-            <Tooltip key="pinWithView" title={<div className="dash-tooltip">Pin selected region to trail</div>} placement="bottom">
-                <button className="antimodeMenu-button" onPointerDown={this.pinWithView}>
-                    {presPinWithViewIcon}
-                </button>
-            </Tooltip>,
-        ];
+        const buttons = (
+            <>
+                <Tooltip key="collect" title={<div className="dash-tooltip">Create a Collection</div>} placement="bottom">
+                    <button className="antimodeMenu-button" onPointerDown={this.createCollection}>
+                        <FontAwesomeIcon icon="object-group" size="lg" />
+                    </button>
+                </Tooltip>
+                ,
+                <Tooltip key="group" title={<div className="dash-tooltip">Create a Grouping</div>} placement="bottom">
+                    <button className="antimodeMenu-button" onPointerDown={e => this.createCollection(e, true)}>
+                        <FontAwesomeIcon icon="layer-group" size="lg" />
+                    </button>
+                </Tooltip>
+                ,
+                <Tooltip key="summarize" title={<div className="dash-tooltip">Summarize Documents</div>} placement="bottom">
+                    <button className="antimodeMenu-button" onPointerDown={this.summarize}>
+                        <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
+                    </button>
+                </Tooltip>
+                ,
+                <Tooltip key="delete" title={<div className="dash-tooltip">Delete Documents</div>} placement="bottom">
+                    <button className="antimodeMenu-button" onPointerDown={this.delete}>
+                        <FontAwesomeIcon icon="trash-alt" size="lg" />
+                    </button>
+                </Tooltip>
+                ,
+                <Tooltip key="pinWithView" title={<div className="dash-tooltip">Pin selected region to trail</div>} placement="bottom">
+                    <button className="antimodeMenu-button" onPointerDown={this.pinWithView}>
+                        {presPinWithViewIcon}
+                    </button>
+                </Tooltip>
+            </>
+        );
         return this.getElement(buttons);
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 054d01d8e..b94db2c6b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -696,6 +696,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
     });
 
+    @action
     onPointerDown = (e: React.PointerEvent): void => {
         if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return;
         // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
@@ -736,6 +737,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             document.removeEventListener('pointerup', this.onPointerUp);
             document.addEventListener('pointerup', this.onPointerUp);
+        } else {
+            this._cursorTimer && clearTimeout(this._cursorTimer);
+            this._cursorPress = false;
         }
     };
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index e477d7ae2..1de29f806 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -161,7 +161,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 </div>
             );
             return (
-                <div className={`menuButton ${this.type} ${numBtnType}`} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}>
+                <div className={`menuButton ${this.type} ${numBtnType}`} onPointerDown={e => e.stopPropagation()} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}>
                     {checkResult}
                     {label}
                     {this.rootDoc.dropDownOpen ? dropdown : null}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 63347015b..39005a18b 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -287,12 +287,12 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
         document.addEventListener('pointerdown', hideMenu, true);
     };
     render() {
-        return this.getElement([
+        return this.getElement(
             <Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}>
                 <button className="antimodeMenu-button" onPointerDown={this.showFields}>
                     <FontAwesomeIcon icon="eye" size="lg" />
                 </button>
-            </Tooltip>,
-        ]);
+            </Tooltip>
+        );
     }
 }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index deb1d68a7..8407eee96 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -693,6 +693,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     @undoBatch
     makeTargetToggle = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle);
 
+    @undoBatch
+    showTargetTrail = (anchor: Doc) => {
+        const trail = DocCast(anchor.presTrail);
+        if (trail) {
+            Doc.ActivePresentation = trail;
+            this.props.addDocTab(trail, OpenWhere.replaceRight);
+        }
+    };
+
     isTargetToggler = (anchor: Doc) => BoolCast(anchor.followLinkToggle);
 
     specificContextMenu = (e: React.MouseEvent): void => {
@@ -717,6 +726,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         AnchorMenu.Instance.Pinned = false;
                         AnchorMenu.Instance.PinToPres = () => this.pinToPres(anchor as Doc);
                         AnchorMenu.Instance.MakeTargetToggle = () => this.makeTargetToggle(anchor as Doc);
+                        AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(anchor as Doc);
                         AnchorMenu.Instance.IsTargetToggler = () => this.isTargetToggler(anchor as Doc);
                         AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
                     })
@@ -1471,7 +1481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (!this._editorView?.state.selection.empty && !(this._editorView?.state.selection instanceof NodeSelection) && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu();
         if (!this._downEvent) return;
         this._downEvent = false;
-        if (this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
+        if (this._editorView?.state.selection.empty && this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
             const editor = this._editorView!;
             const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
             !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 8d43c33f0..3898490d3 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -108,7 +108,7 @@ export const marks: { [index: string]: MarkSpec } = {
                           node.attrs.title,
                       ],
                   ]
-                : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
+                : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline; cursor: default` }, 0];
         },
     },
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 226fd8d7d..4a60c99ec 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -129,8 +129,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentationTargetDoc === layoutDoc;
     clearSelectedArray = () => this.selectedArray.clear();
-    addToSelectedArray = (doc: Doc) => this.selectedArray.add(doc);
-    removeFromSelectedArray = (doc: Doc) => this.selectedArray.delete(doc);
+    addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
+    removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));
 
     _unmounting = false;
     @action
@@ -211,12 +211,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     nextSlide = (slideNum?: number) => {
         const nextSlideInd = slideNum ?? this.itemIndex + 1;
         let curSlideInd = nextSlideInd;
-        const resetSelection = action(() => {
-            this.clearSelectedArray();
-            for (let i = nextSlideInd; i <= curSlideInd; i++) {
-                this.addToSelectedArray(this.childDocs[i]);
-            }
-        });
         CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
         this.clearSelectedArray();
         const doGroupWithUp =
@@ -224,6 +218,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             () => {
                 if (nextSelected < this.childDocs.length) {
                     if (force || this.childDocs[nextSelected].groupWithUp) {
+                        this.addToSelectedArray(this.childDocs[nextSelected]);
                         const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1;
                         if (serial) {
                             this.gotoDocument(nextSelected, this.activeItem, true, async () => {
@@ -232,7 +227,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 doGroupWithUp(nextSelected + 1)();
                             });
                         } else {
-                            this.gotoDocument(nextSelected, this.activeItem, undefined, resetSelection);
+                            this.gotoDocument(nextSelected, this.activeItem, true);
                             curSlideInd = this.itemIndex;
                             doGroupWithUp(nextSelected + 1)();
                         }
@@ -513,24 +508,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             console.log('Finish Slide Nav: ' + targetDoc.title);
             targetDoc[AnimationSym] = undefined;
         };
-        const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null);
-        const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
-        const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined;
-        const includesDoc = () => (DocumentManager.Instance.getDocumentView(targetDoc) ? true : false); //   DocListCast(presCollection?.data).includes(targetDoc);
-        const tabMap = CollectionDockingView.Instance?.tabMap;
-        const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc === srcContext || tab.DashDoc === targetDoc);
-        // Handles the setting of presCollection
-        if (includesDoc()) {
-            //Case 1: Pres collection should not change as it is already the same
-        } else if (tab !== undefined) {
-            // Case 2: Pres collection should update
-            this.layoutDoc.presCollection = srcContext;
-        }
         const selViewCache = Array.from(this.selectedArray);
         const dragViewCache = Array.from(this._dragArray);
         const eleViewCache = Array.from(this._eleArray);
         const resetSelection = action(() => {
-            if (!includesDoc()) {
+            if (!this.props.isSelected()) {
                 const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
                 if (presDocView) SelectionManager.SelectView(presDocView, false);
                 this.clearSelectedArray();
@@ -542,17 +524,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         const createDocView = (doc: Doc, finished?: () => void) => {
             DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.());
-            (collectionDocView ?? this).props.addDocTab(doc, OpenWhere.lightbox);
-            this.layoutDoc.presCollection = targetDoc;
+            LightboxView.AddDocTab(doc, OpenWhere.lightbox);
         };
-        PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc() || tab ? finished : resetSelection);
+        PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, resetSelection);
     };
 
-    static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, srcContext: Doc, finished?: () => void) {
+    static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, finished?: () => void) {
         if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
             (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
             return;
         }
+        const options: DocFocusOptions = {
+            willPan: activeItem.presMovement !== PresMovement.None,
+            willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
+            zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
+            zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
+            effect: activeItem,
+            noSelect: true,
+            originatingDoc: activeItem,
+            easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
+            zoomTextSelections: BoolCast(activeItem.presZoomText),
+            playAudio: BoolCast(activeItem.presPlayAudio),
+        };
         const restoreLayout = () => {
             // After navigating to the document, if it is added as a presPinView then it will
             // adjust the pan and scale to that of the pinView when it was added.
@@ -562,47 +555,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500));
             }
         };
-        // If openDocument is selected then it should open the document for the user
-        if (activeItem.openDocument) {
-            LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc);
-            setTimeout(restoreLayout);
-        } else {
-            if (targetDoc) {
-                const options: DocFocusOptions = {
-                    willPan: activeItem.presMovement !== PresMovement.None,
-                    willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center,
-                    zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1),
-                    zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500),
-                    effect: activeItem,
-                    noSelect: true,
-                    originatingDoc: activeItem,
-                    easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
-                    zoomTextSelections: BoolCast(activeItem.presZoomText),
-                    playAudio: BoolCast(activeItem.presPlayAudio),
-                };
-                if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
-                var containerDocContext = srcContext ? [srcContext] : [];
-                while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
-                    containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
-                }
-                const testTarget = containerDocContext.length ? containerDocContext[0] : targetDoc;
-                if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(testTarget)) {
-                    DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
-                        if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(LightboxView.LightboxDoc)) {
-                            DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
-                            restoreLayout();
-                        } else {
-                            LightboxView.SetLightboxDoc(undefined);
-                            DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
-                            restoreLayout();
-                        }
-                    });
-                    return;
-                }
-                DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished);
-                restoreLayout();
-            } else restoreLayout();
+        const finishAndRestoreLayout = () => {
+            finished?.();
+            restoreLayout();
+        };
+        const containerDocContext = DocumentManager.GetContextPath(targetDoc);
+
+        let context = containerDocContext.length ? containerDocContext[0] : targetDoc;
+        if (activeItem.presOpenInLightbox) {
+            if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(DocCast(targetDoc.annotationOn) ?? targetDoc))) {
+                context = DocCast(targetDoc.annotationOn) ?? targetDoc;
+                LightboxView.SetLightboxDoc(context); // openInTab(targetDoc);
+            }
         }
+        if (targetDoc) {
+            if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined;
+
+            DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => {
+                if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(context.annotationOn) ?? context)) {
+                    LightboxView.SetLightboxDoc(undefined);
+                }
+                DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finishAndRestoreLayout);
+            });
+        } else finishAndRestoreLayout();
     }
 
     /**
@@ -615,38 +590,34 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
             const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
-            if (tagDoc === this.layoutDoc.presCollection) {
-                tagDoc.opacity = 1;
-            } else {
-                if (curDoc.presHide) {
-                    if (index !== this.itemIndex) {
-                        tagDoc.opacity = 1;
-                    }
+            if (curDoc.presHide) {
+                if (index !== this.itemIndex) {
+                    tagDoc.opacity = 1;
                 }
-                const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
-                if (curDoc.presHideBefore && index === hidingIndBef) {
-                    if (index > this.itemIndex) {
-                        tagDoc.opacity = 0;
-                    } else if (index === this.itemIndex || !curDoc.presHideAfter) {
-                        tagDoc.opacity = 1;
-                    }
+            }
+            const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex);
+            if (curDoc.presHideBefore && index === hidingIndBef) {
+                if (index > this.itemIndex) {
+                    tagDoc.opacity = 0;
+                } else if (index === this.itemIndex || !curDoc.presHideAfter) {
+                    tagDoc.opacity = 1;
                 }
-                const hidingIndAft = itemIndexes
-                    .slice()
-                    .reverse()
-                    .find(item => item < this.itemIndex);
-                if (curDoc.presHideAfter && index === hidingIndAft) {
-                    if (index < this.itemIndex) {
-                        tagDoc.opacity = 0;
-                    } else if (index === this.itemIndex || !curDoc.presHideBefore) {
-                        tagDoc.opacity = 1;
-                    }
+            }
+            const hidingIndAft = itemIndexes
+                .slice()
+                .reverse()
+                .find(item => item < this.itemIndex);
+            if (curDoc.presHideAfter && index === hidingIndAft) {
+                if (index < this.itemIndex) {
+                    tagDoc.opacity = 0;
+                } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+                    tagDoc.opacity = 1;
                 }
-                const hidingInd = itemIndexes.find(item => item === this.itemIndex);
-                if (curDoc.presHide && index === hidingInd) {
-                    if (index === this.itemIndex) {
-                        tagDoc.opacity = 0;
-                    }
+            }
+            const hidingInd = itemIndexes.find(item => item === this.itemIndex);
+            if (curDoc.presHide && index === hidingInd) {
+                if (index === this.itemIndex) {
+                    tagDoc.opacity = 0;
                 }
             }
         });
@@ -894,8 +865,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     selectElement = async (doc: Doc) => {
         CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
         this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
-        if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
-        else this.updateCurrentPresentation(DocCast(doc.context));
+        // if (doc.presPinView) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
+        // else
+        this.updateCurrentPresentation(DocCast(doc.context));
     };
 
     //Command click
@@ -1043,7 +1015,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get order() {
         const order: JSX.Element[] = [];
         const docs: Doc[] = [];
-        const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
+        const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement();
         const dv = DocumentManager.Instance.getDocumentView(presCollection);
         this.childDocs
             .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
@@ -1209,8 +1181,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     updateOpenDoc = (activeItem: Doc) => {
-        activeItem.openDocument = !activeItem.openDocument;
-        this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument));
+        activeItem.presOpenInLightbox = !activeItem.presOpenInLightbox;
+        this.selectedArray.forEach(doc => (doc.presOpenInLightbox = activeItem.presOpenInLightbox));
     };
     @undoBatch
     @action
@@ -1255,8 +1227,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get transitionDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const isPresCollection: boolean = targetDoc === this.layoutDoc.presCollection;
-        const isPinWithView: boolean = BoolCast(activeItem.presPinView);
         const presEffect = (effect: PresEffect) => (
             <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}>
                 {effect}
@@ -1309,11 +1279,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             {this.movementName(activeItem)}
                             <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
                             <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}>
-                                {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.None)}
+                                {presMovement(PresMovement.None)}
                                 {presMovement(PresMovement.Center)}
                                 {presMovement(PresMovement.Zoom)}
                                 {presMovement(PresMovement.Pan)}
-                                {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.Jump)}
+                                {presMovement(PresMovement.Jump)}
                             </div>
                         </div>
                         <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
@@ -1355,29 +1325,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="ribbon-box">
                         Visibility {'&'} Duration
                         <div className="ribbon-doubleButton">
-                            {isPresCollection ? null : (
-                                <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
-                                    <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
-                                        Hide before
-                                    </div>
-                                </Tooltip>
-                            )}
-                            {isPresCollection ? null : (
-                                <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
-                                    <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
-                                        Hide
-                                    </div>
-                                </Tooltip>
-                            )}
-                            {isPresCollection ? null : (
-                                <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
-                                    <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
-                                        Hide after
-                                    </div>
-                                </Tooltip>
-                            )}
+                            <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
+                                <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+                                    Hide before
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
+                                <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+                                    Hide
+                                </div>
+                            </Tooltip>
+
+                            <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
+                                <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+                                    Hide after
+                                </div>
+                            </Tooltip>
+
                             <Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
-                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presOpenInLightbox ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
                                     Lightbox
                                 </div>
                             </Tooltip>
@@ -1412,48 +1378,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </>
                         )}
                     </div>
-                    {isPresCollection ? null : (
-                        <div className="ribbon-box">
-                            Effects
-                            <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
-                                <div className="presBox-subheading">Play Audio Annotation</div>
-                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} />
-                            </div>
-                            <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
-                                <div className="presBox-subheading">Zoom Text Selections</div>
-                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />
-                            </div>
-                            <div
-                                className="presBox-dropdown"
-                                onClick={action(e => {
-                                    e.stopPropagation();
-                                    this._openEffectDropdown = !this._openEffectDropdown;
-                                })}
-                                style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
-                                {effect?.toString()}
-                                <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
-                                <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
-                                    {presEffect(PresEffect.None)}
-                                    {presEffect(PresEffect.Fade)}
-                                    {presEffect(PresEffect.Flip)}
-                                    {presEffect(PresEffect.Rotate)}
-                                    {presEffect(PresEffect.Bounce)}
-                                    {presEffect(PresEffect.Roll)}
-                                </div>
-                            </div>
-                            <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
-                                <div className="presBox-subheading">Effect direction</div>
-                                <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
-                            </div>
-                            <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
-                                {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
-                                {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
-                                {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
-                                {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
-                                {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+                    <div className="ribbon-box">
+                        Effects
+                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+                            <div className="presBox-subheading">Play Audio Annotation</div>
+                            <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} />
+                        </div>
+                        <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+                            <div className="presBox-subheading">Zoom Text Selections</div>
+                            <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />
+                        </div>
+                        <div
+                            className="presBox-dropdown"
+                            onClick={action(e => {
+                                e.stopPropagation();
+                                this._openEffectDropdown = !this._openEffectDropdown;
+                            })}
+                            style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                            {effect?.toString()}
+                            <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+                            <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+                                {presEffect(PresEffect.None)}
+                                {presEffect(PresEffect.Fade)}
+                                {presEffect(PresEffect.Flip)}
+                                {presEffect(PresEffect.Rotate)}
+                                {presEffect(PresEffect.Bounce)}
+                                {presEffect(PresEffect.Roll)}
                             </div>
                         </div>
-                    )}
+                        <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
+                            <div className="presBox-subheading">Effect direction</div>
+                            <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
+                        </div>
+                        <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
+                            {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+                            {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+                            {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+                            {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+                            {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+                        </div>
+                    </div>
                     <div className="ribbon-final-box">
                         <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
                             Apply to all
@@ -1658,7 +1622,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get newDocumentToolbarDropdown() {
         return (
             <div
-                className={'presBox-toolbar-dropdown'}
+                className="presBox-toolbar-dropdown"
                 style={{ display: this._newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
                 onClick={e => e.stopPropagation()}
                 onPointerUp={e => e.stopPropagation()}
@@ -1794,7 +1758,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (freeform && layout) doc = this.createTemplate(layout, title);
         if (!freeform && !layout) doc = Docs.Create.TextDocument('', { _nativeWidth: 400, _width: 225, title: title });
         if (doc) {
-            const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
+            const tabMap = CollectionDockingView.Instance?.tabMap;
+            const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc;
+            const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentationTargetDoc ?? tab;
             const data = Cast(presCollection?.data, listSpec(Doc));
             const presData = Cast(this.rootDoc.data, listSpec(Doc));
             if (data && presData) {
@@ -2300,12 +2266,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
     }
     static NavigateToDoc(bestTarget: Doc, activeItem: Doc) {
-        const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
         const openInTab = (doc: Doc, finished?: () => void) => {
             CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
             finished?.();
         };
-        PresBox.NavigateToTarget(bestTarget, activeItem, openInTab, srcContext);
+        PresBox.NavigateToTarget(bestTarget, activeItem, openInTab);
     }
 }
 
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f1c97d26a..788900b46 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -365,8 +365,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     // a previously recorded video will have timecode defined
-    static videoIsRecorded = (activeItem: Doc) => {
-        const casted = Cast(activeItem.recording, Doc, null);
+    static videoIsRecorded = (activeItem: Opt<Doc>) => {
+        const casted = Cast(activeItem?.recording, Doc, null);
         return casted && 'currentTimecode' in casted;
     };
 
@@ -381,10 +381,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static removeEveryExistingRecordingInOverlay = () => {
         // Remove every recording that already exists in overlay view
         DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
-            // if it's a recording video, don't remove from overlay (user can lose data)
-            if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return;
-
             if (doc.slides !== null) {
+                // if it's a recording video, don't remove from overlay (user can lose data)
+                if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return;
+
                 Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc);
             }
         });
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 2fb795b06..9af686d83 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -11,13 +11,13 @@ import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
 import { LinkPopup } from '../linking/LinkPopup';
 import { ButtonDropdown } from '../nodes/formattedText/RichTextMenu';
 import './AnchorMenu.scss';
-import { truncate } from 'lodash';
 
 @observer
 export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     static Instance: AnchorMenu;
 
     private _disposer: IReactionDisposer | undefined;
+    private _disposer2: IReactionDisposer | undefined;
     private _commentCont = React.createRef<HTMLButtonElement>();
     private _palette = [
         'rgba(208, 2, 27, 0.8)',
@@ -37,9 +37,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         'rgba(0, 0, 0, 0.8)',
     ];
 
-    @observable private _keyValue: string = '';
-    @observable private _valueValue: string = '';
-    @observable private _added: boolean = false;
     @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';
     @observable private _showLinkPopup: boolean = false;
 
@@ -56,9 +53,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     public Highlight: (color: string, isTargetToggler: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
     public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
     public Delete: () => void = unimplementedFunction;
-    public AddTag: (key: string, value: string) => boolean = returnFalse;
     public PinToPres: () => void = unimplementedFunction;
     public MakeTargetToggle: () => void = unimplementedFunction;
+    public ShowTargetTrail: () => void = unimplementedFunction;
     public IsTargetToggler: () => boolean = returnFalse;
     public get Active() {
         return this._left > 0;
@@ -71,7 +68,17 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         AnchorMenu.Instance._canFade = false;
     }
 
+    componentWillUnmount() {
+        this._disposer?.();
+        this._disposer2?.();
+    }
+
     componentDidMount() {
+        this._disposer2 = reaction(
+            () => this._opacity,
+            opacity => !opacity && (this._showLinkPopup = false),
+            { fireImmediately: true }
+        );
         this._disposer = reaction(
             () => SelectionManager.Views(),
             selected => {
@@ -175,82 +182,62 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         this.highlightColor = Utils.colorString(col);
     };
 
-    @action keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this._keyValue = e.currentTarget.value;
-    };
-    @action valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this._valueValue = e.currentTarget.value;
-    };
-    @action addTag = (e: React.PointerEvent) => {
-        if (this._keyValue.length > 0 && this._valueValue.length > 0) {
-            this._added = this.AddTag(this._keyValue, this._valueValue);
-            setTimeout(
-                action(() => (this._added = false)),
-                1000
-            );
-        }
-    };
-
     render() {
         const buttons =
-            this.Status === 'marquee'
-                ? [
-                      this.highlighter,
-
-                      <Tooltip key="annotate" title={<div className="dash-tooltip">{'Drag to Place Annotation'}</div>}>
-                          <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: 'grab' }}>
-                              <FontAwesomeIcon icon="comment-alt" size="lg" />
-                          </button>
-                      </Tooltip>,
-                      AnchorMenu.Instance.OnAudio === unimplementedFunction ? (
-                          <></>
-                      ) : (
-                          <Tooltip key="annoaudiotate" title={<div className="dash-tooltip">{'Click to Record Annotation'}</div>}>
-                              <button className="antimodeMenu-button annotate" onPointerDown={this.audioDown} style={{ cursor: 'grab' }}>
-                                  <FontAwesomeIcon icon="microphone" size="lg" />
-                              </button>
-                          </Tooltip>
-                      ),
-                      <Tooltip key="link" title={<div className="dash-tooltip">{'Find document to link to selected text'}</div>}>
-                          <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup} style={{}}>
-                              <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
-                              <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'top left', top: 12, left: 12 }} icon={'link'} size="lg" />
-                          </button>
-                      </Tooltip>,
-                      <LinkPopup key="popup" showPopup={this._showLinkPopup} linkCreateAnchor={this.onMakeAnchor} />,
-                      AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? (
-                          <></>
-                      ) : (
-                          <Tooltip key="crop" title={<div className="dash-tooltip">{'Click/Drag to create cropped image'}</div>}>
-                              <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: 'grab' }}>
-                                  <FontAwesomeIcon icon="image" size="lg" />
-                              </button>
-                          </Tooltip>
-                      ),
-                  ]
-                : [
-                      <Tooltip key="trash" title={<div className="dash-tooltip">{'Remove Link Anchor'}</div>}>
-                          <button className="antimodeMenu-button" onPointerDown={this.Delete}>
-                              <FontAwesomeIcon icon="trash-alt" size="lg" />
-                          </button>
-                      </Tooltip>,
-                      <Tooltip key="Pin" title={<div className="dash-tooltip">{'Pin to Presentation'}</div>}>
-                          <button className="antimodeMenu-button" onPointerDown={this.PinToPres}>
-                              <FontAwesomeIcon icon="map-pin" size="lg" />
-                          </button>
-                      </Tooltip>,
-                      <Tooltip key="toggle" title={<div className="dash-tooltip">{'make target visibility toggle on click'}</div>}>
-                          <button className="antimodeMenu-button" style={{ color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }} onPointerDown={this.MakeTargetToggle}>
-                              <FontAwesomeIcon icon="thumbtack" size="lg" />
-                          </button>
-                      </Tooltip>,
-                      // <div key="7" className="anchorMenu-addTag" >
-                      //     <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
-                      //     <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
-                      // </div>,
-                      // <button key="8" className="antimodeMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}>
-                      //     <FontAwesomeIcon style={{ transition: "all .2s" }} color={this._added ? "#42f560" : "white"} icon="check" size="lg" /></button>,
-                  ];
+            this.Status === 'marquee' ? (
+                <>
+                    {this.highlighter}
+                    <Tooltip key="annotate" title={<div className="dash-tooltip">Drag to Place Annotation</div>}>
+                        <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: 'grab' }}>
+                            <FontAwesomeIcon icon="comment-alt" size="lg" />
+                        </button>
+                    </Tooltip>
+                    {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
+                        <Tooltip key="annoaudiotate" title={<div className="dash-tooltip">Click to Record Annotation</div>}>
+                            <button className="antimodeMenu-button annotate" onPointerDown={this.audioDown} style={{ cursor: 'grab' }}>
+                                <FontAwesomeIcon icon="microphone" size="lg" />
+                            </button>
+                        </Tooltip>
+                    )}
+                    <Tooltip key="link" title={<div className="dash-tooltip">Find document to link to selected text</div>}>
+                        <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup}>
+                            <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
+                            <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'top left', top: 12, left: 12 }} icon={'link'} size="lg" />
+                        </button>
+                    </Tooltip>
+                    <LinkPopup key="popup" showPopup={this._showLinkPopup} linkCreateAnchor={this.onMakeAnchor} />,
+                    {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
+                        <Tooltip key="crop" title={<div className="dash-tooltip">Click/Drag to create cropped image</div>}>
+                            <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: 'grab' }}>
+                                <FontAwesomeIcon icon="image" size="lg" />
+                            </button>
+                        </Tooltip>
+                    )}
+                </>
+            ) : (
+                <>
+                    <Tooltip key="trash" title={<div className="dash-tooltip">Remove Link Anchor</div>}>
+                        <button className="antimodeMenu-button" onPointerDown={this.Delete}>
+                            <FontAwesomeIcon icon="trash-alt" size="lg" />
+                        </button>
+                    </Tooltip>
+                    <Tooltip key="Pin" title={<div className="dash-tooltip">Pin to Presentation</div>}>
+                        <button className="antimodeMenu-button" onPointerDown={this.PinToPres}>
+                            <FontAwesomeIcon icon="map-pin" size="lg" />
+                        </button>
+                    </Tooltip>
+                    <Tooltip key="trail" title={<div className="dash-tooltip">Show Linked Trail</div>}>
+                        <button className="antimodeMenu-button" onPointerDown={this.ShowTargetTrail}>
+                            <FontAwesomeIcon icon="taxi" size="lg" />
+                        </button>
+                    </Tooltip>
+                    <Tooltip key="toggle" title={<div className="dash-tooltip">make target visibility toggle on click</div>}>
+                        <button className="antimodeMenu-button" style={{ color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }} onPointerDown={this.MakeTargetToggle}>
+                            <FontAwesomeIcon icon="thumbtack" size="lg" />
+                        </button>
+                    </Tooltip>
+                </>
+            );
 
         return this.getElement(buttons);
     }
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index d8e44ae9d..0a8c69881 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -65,7 +65,6 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             AnchorMenu.Instance.Status = 'annotation';
             AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
             AnchorMenu.Instance.Pinned = false;
-            AnchorMenu.Instance.AddTag = this.addTag.bind(this);
             AnchorMenu.Instance.PinToPres = this.pinToPres;
             AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
             AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
@@ -77,12 +76,6 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         }
     };
 
-    addTag = (key: string, value: string): boolean => {
-        const valNum = parseInt(value);
-        this.annoTextRegion[key] = isNaN(valNum) ? value : valNum;
-        return true;
-    };
-
     render() {
         const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
         return (
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index aac488559..7e2a5a237 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -10,6 +10,7 @@ import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { LinkManager } from '../../util/LinkManager';
+import { undoBatch } from '../../util/UndoManager';
 import { CollectionDockingView } from '../collections/CollectionDockingView';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
@@ -113,14 +114,12 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, false));
     });
 
-    // TODO: nda -- Change this method to change what happens when you click on the item.
+    @undoBatch
     makeLink = action((linkTo: Doc) => {
-        if (this.props.linkCreateAnchor) {
-            const linkFrom = this.props.linkCreateAnchor();
-            if (linkFrom) {
-                const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
-                link && this.props.linkCreated?.(link);
-            }
+        const linkFrom = this.props.linkCreateAnchor?.();
+        if (linkFrom) {
+            const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
+            link && this.props.linkCreated?.(link);
         }
     });
 
-- 
cgit v1.2.3-70-g09d2


From af2a2c83868c87812e9ae54c8e3cced81374619a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 27 Feb 2023 15:08:45 -0500
Subject: restructured getAnchor()/scrollFocus to be more consistent.  added
 setterscript for computedFields. restructed getFieldsImpl to avoid making
 multiple requests for the same document due to timing issues by 'locking' a
 document cache with a promise before sending the server request.  added
 rotation and fill color as animatable fields.  fixed image cropping for

---
 src/client/DocServer.ts                            | 143 +++++++-------
 src/client/documents/Documents.ts                  |  23 ++-
 src/client/util/DocumentManager.ts                 |   4 +-
 src/client/util/LinkManager.ts                     |   2 +-
 src/client/util/SharingManager.tsx                 |  30 ++-
 src/client/views/DocumentButtonBar.tsx             |  10 +-
 src/client/views/InkTangentHandles.tsx             |   9 +-
 src/client/views/InkingStroke.tsx                  |  32 +++-
 src/client/views/MainView.tsx                      |   1 -
 src/client/views/MarqueeAnnotator.tsx              |  35 ++--
 src/client/views/StyleProvider.tsx                 |   3 +
 .../collections/CollectionStackedTimeline.tsx      |  10 +-
 .../views/collections/CollectionTimeView.tsx       |  37 +---
 src/client/views/collections/TabDocView.tsx        |   8 +-
 .../CollectionFreeFormLayoutEngines.tsx            |   2 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  49 ++---
 src/client/views/nodes/AudioBox.tsx                |  12 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  23 ++-
 src/client/views/nodes/DocumentLinksButton.tsx     |  20 +-
 src/client/views/nodes/DocumentView.tsx            |  15 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |   6 +-
 src/client/views/nodes/ImageBox.tsx                |  34 +---
 src/client/views/nodes/LabelBox.tsx                |   2 -
 src/client/views/nodes/PDFBox.tsx                  |  27 +--
 src/client/views/nodes/ScreenshotBox.tsx           |   2 +-
 src/client/views/nodes/ScriptingBox.tsx            |   5 +-
 src/client/views/nodes/VideoBox.tsx                |  23 +--
 src/client/views/nodes/WebBox.tsx                  |  40 ++--
 src/client/views/nodes/button/FontIconBox.tsx      |  28 ++-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   4 +-
 src/client/views/nodes/trails/PresBox.tsx          | 211 ++++++++++++++-------
 src/client/views/pdf/AnchorMenu.tsx                |   7 +-
 src/client/views/pdf/PDFViewer.tsx                 |   9 +-
 src/fields/Doc.ts                                  |  28 ++-
 src/fields/ScriptField.ts                          |   6 +-
 src/server/DashUploadUtils.ts                      |   2 +-
 src/server/websocket.ts                            |   2 +-
 37 files changed, 517 insertions(+), 387 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index cab90138f..8f79ebb03 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -234,7 +234,7 @@ export namespace DocServer {
      * the server if the document has not been cached.
      * @param id the id of the requested document
      */
-    const _GetRefFieldImpl = async (id: string, force: boolean = false): Promise<Opt<RefField>> => {
+    const _GetRefFieldImpl = (id: string, force: boolean = false): Promise<Opt<RefField>> => {
         // an initial pass through the cache to determine whether the document needs to be fetched,
         // is already in the process of being fetched or already exists in the
         // cache
@@ -309,8 +309,7 @@ export namespace DocServer {
     }
 
     export async function getYoutubeChannels() {
-        const apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
-        return apiKey;
+        return await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
     }
 
     export function getYoutubeVideos(videoTitle: string, callBack: (videos: any[]) => void) {
@@ -329,10 +328,11 @@ export namespace DocServer {
      */
     const _GetRefFieldsImpl = async (ids: string[]): Promise<{ [id: string]: Opt<RefField> }> => {
         const requestedIds: string[] = [];
-        const waitingIds: string[] = [];
-        const promises: Promise<Opt<RefField>>[] = [];
-        const map: { [id: string]: Opt<RefField> } = {};
+        const promises: Promise<any>[] = [];
 
+        let defaultRes: any = undefined;
+        const defaultPromise = new Promise<any>(res => (defaultRes = res));
+        let defaultPromises: { p: Promise<any>; id: string }[] = [];
         // 1) an initial pass through the cache to determine
         // i) which documents need to be fetched
         // ii) which are already in the process of being fetched
@@ -340,6 +340,13 @@ export namespace DocServer {
         for (const id of ids) {
             const cached = _cache[id];
             if (cached === undefined) {
+                defaultPromises.push({
+                    id,
+                    p: (_cache[id] = new Promise<any>(async res => {
+                        await defaultPromise;
+                        res(_cache[id]);
+                    })),
+                });
                 // NOT CACHED => we'll have to send a request to the server
                 requestedIds.push(id);
             } else if (cached instanceof Promise) {
@@ -347,10 +354,10 @@ export namespace DocServer {
                 // and requested one of the documents I'm looking for. Shouldn't fetch again, just
                 // wait until this promise is resolved (see 7)
                 promises.push(cached);
-                waitingIds.push(id);
+                // waitingIds.push(id);
             } else {
                 // CACHED => great, let's just add it to the field map
-                map[id] = cached;
+                // map[id] = cached;
             }
         }
 
@@ -358,74 +365,65 @@ export namespace DocServer {
             // 2) synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string)
             // fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
             // the fields have been returned from the server
-            console.log('Requesting ' + requestedIds.length + ' fields');
+            console.log('Requesting ' + requestedIds.length);
             FieldLoader.active && runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length));
-            const getSerializedFields: Promise<any> = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
+            const serializedFields = await Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
 
             // 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
             // Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all
             // future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
-
-            let retrieved = 0;
-            const fields: { [id: string]: RefField } = {};
-            await getSerializedFields.then(async fieldvals => {
-                console.log('deserializing ' + fieldvals.length + ' fields');
-                const proms: Promise<void>[] = [];
-                await runInAction(async () => {
-                    for (const field of fieldvals) {
-                        const cached = _cache[field.id];
-                        if (!cached) {
-                            retrieved++;
-                            if (FieldLoader.active && retrieved % 150 === 0) {
-                                runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = retrieved));
-                                await new Promise(res => setTimeout(res));
+            let processed = 0;
+            console.log('deserializing ' + serializedFields.length + ' fields');
+            for (const field of serializedFields) {
+                processed++;
+                if (FieldLoader.active && processed % 150 === 0) {
+                    runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = processed));
+                    await new Promise(res => setTimeout(res)); // force loading to yield to splash screen rendering to update progress
+                }
+                const cached = _cache[field.id];
+                if (!cached || (cached instanceof Promise && defaultPromises.some(dp => dp.p === cached))) {
+                    // deserialize
+                    // adds to a list of promises that will be awaited asynchronously
+                    promises.push(
+                        (_cache[field.id] = SerializationHelper.Deserialize(field).then(deserialized => {
+                            //overwrite or delete any promises (that we inserted as flags
+                            // to indicate that the field was in the process of being fetched). Now everything
+                            // should be an actual value within or entirely absent from the cache.
+                            if (deserialized !== undefined) {
+                                _cache[field.id] = deserialized;
+                            } else {
+                                delete _cache[field.id];
                             }
-                            // deserialize
-                            const prom = SerializationHelper.Deserialize(field).then(async deserialized => {
-                                fields[field.id] = deserialized;
-
-                                //overwrite or delete any promises (that we inserted as flags
-                                // to indicate that the field was in the process of being fetched). Now everything
-                                // should be an actual value within or entirely absent from the cache.
-                                if (deserialized !== undefined) {
-                                    _cache[field.id] = deserialized;
-                                } else {
-                                    delete _cache[field.id];
-                                }
-                                return deserialized;
-                            });
-                            // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
-                            // we set the value at the field's id to a promise that will resolve to the field.
-                            // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
-                            // The mapping in the .then call ensures that when other callers await these promises, they'll
-                            // get the resolved field
-                            _cache[field.id] = prom;
-
-                            // adds to a list of promises that will be awaited asynchronously
-                            proms.push(prom);
-                        } else if (cached instanceof Promise) {
-                            console.log('.');
-                            proms.push(cached as any);
-                            cached.then((f: any) => (fields[field.id] = f));
-                        } else if (field) {
-                            console.log('-');
-                            proms.push(cached as any);
-                            fields[field.id] = DocServer.GetCachedRefField(field.id) || field;
-                        }
-                    }
-                });
-                return Promise.all(proms);
-            });
+                            const promInd = defaultPromises.findIndex(dp => dp.id === field.id);
+                            promInd !== -1 && defaultPromises.splice(promInd, 1);
+                            return deserialized;
+                        }))
+                    );
+                    // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
+                    // we set the value at the field's id to a promise that will resolve to the field.
+                    // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
+                    // The mapping in the .then call ensures that when other callers await these promises, they'll
+                    // get the resolved field
+                } else if (cached instanceof Promise) {
+                    console.log('.');
+                    //promises.push(cached);
+                } else if (field) {
+                    // console.log('-');
+                }
+            }
+        }
 
-            // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
-            // prototype documents, if any, have also been fetched and cached.
-            console.log('Deserialized ' + Object.keys(fields).length + ' fields');
+        await Promise.all(promises);
+        defaultPromises.forEach(df => delete _cache[df.id]);
+        defaultRes();
 
-            // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
-            // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
-            // id to the soon-to-be-returned field mapping.
-            requestedIds.forEach(id => (map[id] = fields[id]));
-        }
+        // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
+        // prototype documents, if any, have also been fetched and cached.
+        console.log('Deserialized ' + (requestedIds.length - defaultPromises.length) + ' fields');
+        // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
+        // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
+        // id to the soon-to-be-returned field mapping.
+        //ids.forEach(id => (map[id] = _cache[id] as any));
 
         // 7) those promises we encountered in the else if of 1), which represent
         // other callers having already submitted a request to the server for (a) document(s)
@@ -434,16 +432,19 @@ export namespace DocServer {
         //
         // fortunately, those other callers will also hit their own version of 6) and clean up
         // the shared cache when these promises resolve, so all we have to do is...
-        const otherCallersFetching = await Promise.all(promises);
+        //const otherCallersFetching = await Promise.all(promises);
         // ...extract the RefFields returned from the resolution of those promises and add them to our
         // own map.
-        waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
+        // waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
 
         // now, we return our completed mapping from all of the ids that were passed into the method
         // to their actual RefField | undefined values. This return value either becomes the input
         // argument to the caller's promise (i.e. GetRefFields(["_id1_", "_id2_", "_id3_"]).then(map => //do something with map...))
         // or it is the direct return result if the promise is awaited (i.e. let fields = await GetRefFields(["_id1_", "_id2_", "_id3_"])).
-        return map;
+        return ids.reduce((map, id) => {
+            map[id] = _cache[id] as any;
+            return map;
+        }, {} as { [id: string]: Opt<RefField> });
     };
 
     let _GetRefFields: (ids: string[]) => Promise<{ [id: string]: Opt<RefField> }> = errorFunc;
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index debb11066..d2ab67849 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -263,6 +263,7 @@ export class DocumentOptions {
     viewTransitionTime?: number; // transition duration for view parameters
     presTransition?: number; //the time taken for the transition TO a document
     presDuration?: number; //the duration of the slide in presentation view
+    presZoomText?: boolean; // whether text anchors should shown in a larger box when following links to make them stand out
     borderRounding?: string;
     boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
     data?: any;
@@ -1043,6 +1044,9 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), url, options, id);
         }
 
+        export function CollectionAnchorDocument(options: DocumentOptions = {}, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+        }
         export function TextanchorDocument(options: DocumentOptions = {}, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
         }
@@ -1051,6 +1055,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
         }
 
+        export function InkAnchorDocument(options: DocumentOptions, id?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+        }
+
         export function HTMLAnchorDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), options, id);
         }
@@ -1407,12 +1415,15 @@ export namespace DocUtils {
                 }
             });
         funcs &&
-            Object.keys(funcs).map(key => {
-                const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-                if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) {
-                    doc[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }) : undefined;
-                }
-            });
+            Object.keys(funcs)
+                .filter(key => key.endsWith('-setter'))
+                .map(key => {
+                    const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+                    if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) {
+                        const setFunc = Cast(funcs[key + '-setter'], 'string', null);
+                        doc[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }, setFunc) : undefined;
+                    }
+                });
         return doc;
     }
     export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index e4d48d4de..d9273c2c9 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -199,7 +199,7 @@ export class DocumentManager {
     static GetContextPath(doc: Opt<Doc>, includeExistingViews?: boolean) {
         if (!doc) return [];
         const srcContext = Cast(doc.context, Doc, null) ?? Cast(Cast(doc.annotationOn, Doc, null)?.context, Doc, null);
-        var containerDocContext = srcContext ? [srcContext] : [];
+        var containerDocContext = srcContext ? [srcContext, doc] : [doc];
         while (
             containerDocContext.length &&
             containerDocContext[0]?.context &&
@@ -286,7 +286,7 @@ export class DocumentManager {
                 return annoContainerView.iconify(() => DocumentManager.Instance.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(targetDoc, { ...options, originalTarget, toggleTarget: false }, createViewFunc, docContextPath, finished)), 30);
             }
             if (!docView && targetDoc.type !== DocumentType.MARKER) {
-                annoContainerView.focus(targetDoc, {}); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+                annoContainerView.focus(targetDoc, options); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
             }
         }
 
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 7da16ca78..46d44fea4 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -48,7 +48,7 @@ export class LinkManager {
                         const a2 = DocCast(linkdata[1]);
                         a1 &&
                             a2 &&
-                            Promise.all([a1.proto, a2.proto]).then(
+                            Promise.all([Doc.GetProto(a1), Doc.GetProto(a2)]).then(
                                 action(protos => {
                                     (protos[0] as Doc)?.[DirectLinksSym].add(link);
                                     (protos[1] as Doc)?.[DirectLinksSym].add(link);
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 00ae85d12..36095700c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -130,29 +130,21 @@ export class SharingManager extends React.Component<{}> {
         if (!this.populating && Doc.UserDoc()[Id] !== '__guest__') {
             this.populating = true;
             const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
-            const raw = JSON.parse(userList) as User[];
-            const sharingDocs: ValidatedUser[] = [];
-            const evaluating = raw.map(async user => {
-                const isCandidate = user.email !== Doc.CurrentUserEmail;
-                if (isCandidate) {
-                    const sharingDoc = await DocServer.GetRefField(user.sharingDocumentId);
-                    const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId);
+            const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== Doc.CurrentUserEmail);
+            const docs = await DocServer.GetRefFields(raw.reduce((list, user) => [...list, user.sharingDocumentId, user.linkDatabaseId], [] as string[]));
+            raw.map(
+                action((newUser: User) => {
+                    const sharingDoc = docs[newUser.sharingDocumentId];
+                    const linkDatabase = docs[newUser.linkDatabaseId];
                     if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
-                        sharingDocs.push({ user, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
-                    }
-                }
-            });
-            return Promise.all(evaluating).then(() => {
-                runInAction(async () => {
-                    for (const sharer of sharingDocs) {
-                        if (!this.users.find(user => user.user.email === sharer.user.email)) {
-                            this.users.push(sharer);
+                        if (!this.users.find(user => user.user.email === newUser.email)) {
+                            this.users.push({ user: newUser, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
                             // LinkManager.addLinkDB(sharer.linkDatabase);
                         }
                     }
-                });
-                this.populating = false;
-            });
+                })
+            );
+            this.populating = false;
         }
     };
 
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index f61d147cf..9298c881c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -315,7 +315,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                                     .views()
                                     .filter(v => v)
                                     .map(dv => dv!.rootDoc);
-                                TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout, pinDocContent, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null), currentFrame: Cast(docs.lastElement()?.currentFrame, 'number', null) });
+                                TabDocView.PinDoc(docs, {
+                                    pinAudioPlay: true,
+                                    pinDocLayout,
+                                    pinData: { dataview: true },
+                                    activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null),
+                                    currentFrame: Cast(docs.lastElement()?.currentFrame, 'number', null),
+                                });
                                 e.stopPropagation();
                             }}
                         />
@@ -496,7 +502,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const rootView = this.props.views()[0];
         const rootDoc = rootView?.rootDoc;
         if (rootDoc) {
-            const anchor = rootView.ComponentView?.getAnchor?.(true) ?? rootDoc;
+            const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc;
             const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
             if (trail !== anchor.presTrail) {
                 DocUtils.MakeLink({ doc: anchor }, { doc: trail }, 'link trail');
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index c4a2f603e..71e0ff63c 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -38,18 +38,19 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
         setupMoveUpEvents(
             this,
             e,
-            (e: PointerEvent, down: number[], delta: number[]) => {
+            action((e: PointerEvent, down: number[], delta: number[]) => {
                 if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('DocDecs move tangent');
                 if (e.altKey) this.onBreakTangent(controlIndex);
                 const inkMoveEnd = this.props.inkView.ptFromScreen({ X: delta[0], Y: delta[1] });
                 const inkMoveStart = this.props.inkView.ptFromScreen({ X: 0, Y: 0 });
                 InkStrokeProperties.Instance.moveTangentHandle(this.docView, -(inkMoveEnd.X - inkMoveStart.X), -(inkMoveEnd.Y - inkMoveStart.Y), handleIndex, oppositeHandleIndex, controlIndex);
                 return false;
-            },
-            () => {
+            }),
+            action(() => {
                 this.props.inkView.controlUndo?.end();
+                this.props.inkView.controlUndo = undefined;
                 UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
-            },
+            }),
             emptyFunction
         );
     };
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 76c1aa80a..17cf6a678 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -25,7 +25,7 @@ import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, HeightSym, WidthSym } from '../../fields/Doc';
 import { InkData, InkField } from '../../fields/InkField';
-import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, RTFCast, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { DashColor, OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { CognitiveServices } from '../cognitive_services/CognitiveServices';
@@ -41,11 +41,13 @@ import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
 import './InkStroke.scss';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { InkTangentHandles } from './InkTangentHandles';
-import { DocComponentView, DocFocusOptions } from './nodes/DocumentView';
+import { DocComponentView, DocFocusOptions, DocumentView } from './nodes/DocumentView';
 import { FieldView, FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { StyleProp } from './StyleProvider';
 import Color = require('color');
+import { Docs } from '../documents/Documents';
+import { PinProps, PresBox } from './nodes/trails';
 
 @observer
 export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -78,12 +80,28 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
     screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.NativeDimScaling?.() || 1);
 
-    getAnchor = (addAsAnnotation: boolean) => {
-        return this._subContentView?.getAnchor?.(addAsAnnotation) || this.rootDoc;
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+        if (this._subContentView) {
+            return this._subContentView.getAnchor?.(addAsAnnotation) || this.rootDoc;
+        }
+
+        if (!addAsAnnotation && !pinProps) return this.rootDoc;
+
+        const anchor = Docs.Create.InkAnchorDocument({ title: 'Ink anchor:' + this.rootDoc.title, presDuration: 1100, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
+        if (anchor) {
+            anchor.backgroundColor = 'transparent';
+            // /* addAsAnnotation &&*/ this.addDocument(anchor);
+            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true, inkable: true } }, this.rootDoc);
+            return anchor;
+        }
+        return this.rootDoc;
     };
 
-    scrollFocus = (textAnchor: Doc, options: DocFocusOptions) => {
-        return this._subContentView?.scrollFocus?.(textAnchor, options);
+    scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => {
+        if (this._subContentView) return this._subContentView?.scrollFocus?.(docView, anchor, options);
+
+        const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
+        return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined;
     };
     /**
      * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space);
@@ -349,7 +367,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1);
         const closed = InkingStroke.IsClosed(inkData);
         const isInkMask = BoolCast(this.layoutDoc.isInkMask);
-        const fillColor = isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : StrCast(this.layoutDoc.fillColor, 'transparent');
+        const fillColor = isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
         const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
 
         // bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be.
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 625dc2748..64f116a09 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -145,7 +145,6 @@ export class MainView extends React.Component {
         if (ele && prog) {
             // remove from DOM
             setTimeout(() => {
-                clearTimeout();
                 prog.style.transition = '1s';
                 prog.style.width = '100%';
             }, 0);
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 3bdf65d01..30867a774 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -8,7 +8,7 @@ import { GetEffectiveAcl } from '../../fields/util';
 import { unimplementedFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { DragManager } from '../util/DragManager';
-import { undoBatch } from '../util/UndoManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
 import './MarqueeAnnotator.scss';
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
@@ -48,7 +48,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     constructor(props: any) {
         super(props);
 
-        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight('', true), true);
+        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => {
+            if (this.props.anchorMenuCrop) {
+                UndoManager.RunInBatch(() => this.props.anchorMenuCrop?.(this.highlight('', true, undefined, false), true), 'cropping');
+            }
+        };
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true));
         AnchorMenu.Instance.OnAudio = unimplementedFunction;
         AnchorMenu.Instance.Highlight = this.highlight;
@@ -87,11 +91,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         AnchorMenu.Instance.StartDrag = action((e: PointerEvent, ele: HTMLElement) => {
             e.preventDefault();
             e.stopPropagation();
-            const sourceAnchorCreator = () => {
-                const annoDoc = this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true); // hyperlink color
-                annoDoc && this.props.addDocument(annoDoc);
-                return annoDoc;
-            };
+            const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color
+
             const targetCreator = (annotationOn: Doc | undefined) => {
                 const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
                 FormattedTextBox.SelectOnLoad = target[Id];
@@ -116,11 +117,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                   e.preventDefault();
                   e.stopPropagation();
                   var cropRegion: Doc | undefined;
-                  const sourceAnchorCreator = () => {
-                      cropRegion = this.highlight('', true); // hyperlink color
-                      cropRegion && this.props.addDocument(cropRegion);
-                      return cropRegion;
-                  };
+                  const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color
                   const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
                   DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                       dragComplete: e => {
@@ -159,7 +156,15 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             return marqueeAnno;
         }
 
-        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, text: this.props.selectionText(), backgroundColor: 'transparent', title: 'Selection on ' + this.props.rootDoc.title });
+        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], {
+            annotationOn: this.props.rootDoc,
+            text: this.props.selectionText(),
+            backgroundColor: 'transparent',
+            presDuration: 2100,
+            presTransition: 500,
+            presZoomText: true,
+            title: 'Selection on ' + this.props.rootDoc.title,
+        });
         let minX = Number.MAX_VALUE;
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
@@ -262,10 +267,6 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
 
             AnchorMenu.Instance.jumpTo(cliX, cliY);
 
-            if (AnchorMenu.Instance.Highlighting) {
-                // when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
-                this.highlight('rgba(245, 230, 95, 0.75)', false); // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
-            }
             this.props.finishMarquee(undefined, undefined, e);
             runInAction(() => (this._width = this._height = 0));
         } else {
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 5f87f0697..153c30052 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -31,6 +31,7 @@ export enum StyleProp {
     BorderRounding = 'borderRounding', // border radius of the document view
     Color = 'color', // foreground color of Document view items
     BackgroundColor = 'backgroundColor', // background color of a document view
+    FillColor = 'fillColor', // fill color of an ink stroke or shape
     WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note
     HideLinkButton = 'hideLinkButton', // hides the blue-dot link button.  used when a document acts like a button
     LinkSource = 'linkSource', // source document of a link -- used by LinkAnchorBox
@@ -158,6 +159,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     )) ||
                 ''
             );
+        case StyleProp.FillColor:
+            return Cast(doc?._fillColor, 'string', Cast(doc?.fillColor, 'string', 'transparent'));
         case StyleProp.Color:
             if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color));
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 28f08b6ce..eecab9d86 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -190,7 +190,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         CollectionStackedTimeline.SelectingRegion = this;
                     } else {
                         this._markerEnd = this.currentTime;
-                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd, undefined, true);
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
                         this._markerEnd = undefined;
                         CollectionStackedTimeline.SelectingRegion = undefined;
                     }
@@ -257,7 +257,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         this._markerEnd = tmp;
                     }
                     if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
-                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd, undefined, true);
+                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
                         setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
                     }
                     (!isClick || !wasSelecting) && (this._markerEnd = undefined);
@@ -273,7 +273,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 undefined,
                 () => {
                     if (shiftKey) {
-                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime, undefined, undefined, true);
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.currentTime, undefined, undefined, true);
                     } else {
                         !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
                     }
@@ -388,8 +388,10 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     // creates marker on timeline
     @undoBatch
     @action
-    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
+    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
         if (anchorStartTime === undefined) return rootDoc;
+        const startTag = '_timecodeToShow';
+        const endTag = '_timecodeToHide';
         const anchor =
             docAnchor ??
             Docs.Create.LabelDocument({
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index e6f29ec37..437b22040 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -21,7 +21,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionSubView } from './CollectionSubView';
 import './CollectionTimeView.scss';
 import React = require('react');
-import { DocFocusOptions } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
 import { PresBox } from '../nodes/trails';
 
 @observer
@@ -52,8 +52,7 @@ export class CollectionTimeView extends CollectionSubView() {
             title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
             annotationOn: this.rootDoc,
         });
-        anchor.presPinPivotField = this.pivotField; // should be captured in pinDocView below
-        PresBox.pinDocView(anchor, { pinData: { viewType: true, filters: true } }, this.rootDoc);
+        PresBox.pinDocView(anchor, { pinData: { viewType: true, pivot: true, filters: true } }, this.rootDoc);
 
         if (addAsAnnotation) {
             // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
@@ -67,32 +66,12 @@ export class CollectionTimeView extends CollectionSubView() {
     };
 
     @action
-    scrollFocus = (anchor: Doc, options: DocFocusOptions) => {
-        if (options.preview) {
-            // if in preview, then override document's fields with view spec
-            this._focusFilters = StrListCast(anchor.presPinDocFilters);
-            this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters);
-            this._focusPivotField = StrCast(anchor.presPinPivotField);
-            return undefined;
-        }
-        const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
-
-        // should be part of restoreTargetDocView
-        this.layoutDoc._prevFilterIndex = 1;
-        this.layoutDoc._pivotField = anchor.presPinPivotField;
-
-        return PresBox.restoreTargetDocView(
-            this.props.DocumentView?.(), //
-            { pinDocLayout: BoolCast(anchor.presPinLayout) },
-            anchor,
-            focusSpeed,
-            {
-                viewType: anchor.presPinViewType ? true : false,
-                filters: anchor.presPinDocFilters || anchor.presPinDocRangeFilters ? true : false,
-            }
-        )
-            ? focusSpeed
-            : undefined;
+    scrollPreview = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => {
+        // if in preview, then override document's fields with view spec
+        this._focusFilters = StrListCast(anchor.presPinDocFilters);
+        this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters);
+        this._focusPivotField = StrCast(anchor.presPinPivotField);
+        return undefined;
     };
 
     layoutEngine = () => this._layoutEngine;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f93810deb..b75f315ca 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -247,13 +247,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 alert('Cannot pin presentation document to itself');
                 return;
             }
-            const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false);
-            const pinDoc = anchorDoc && anchorDoc !== doc ? Doc.MakeDelegate(anchorDoc) : Doc.MakeDelegate(doc);
+            const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps);
+            const pinDoc = Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
             pinDoc.presentationTargetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
             pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
             pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
-            pinDoc.presDuration = 2000;
+            pinDoc.presDuration = pinDoc.presDuration ?? 1000;
             pinDoc.groupWithUp = false;
             pinDoc.context = curPres;
             // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
@@ -274,7 +274,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presStartTime = NumCast(doc.clipStart);
                 pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
             }
-            PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, doc);
+            //PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, anchorDoc && anchorDoc !== doc && !anchorDoc.unrendered ? anchorDoc : doc);
             pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
             Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
             //save position
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 7dd9cdb8b..fa0695fb2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -14,6 +14,7 @@ export interface ViewDefBounds {
     x: number;
     y: number;
     z?: number;
+    rotation?: number;
     text?: string;
     zIndex?: number;
     width?: number;
@@ -31,6 +32,7 @@ export interface PoolData {
     x: number;
     y: number;
     z?: number;
+    rotation?: number;
     zIndex?: number;
     width?: number;
     height?: number;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7d176f426..0477c6a16 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -8,7 +8,6 @@ import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
-import { ObjectField } from '../../../../fields/ObjectField';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { listSpec } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
@@ -41,7 +40,7 @@ import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDo
 import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment } from '../../nodes/DocumentView';
 import { FieldViewProps } from '../../nodes/FieldView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { PresBox } from '../../nodes/trails/PresBox';
+import { PinProps, PresBox } from '../../nodes/trails/PresBox';
 import { VideoBox } from '../../nodes/VideoBox';
 import { CreateImage } from '../../nodes/WebBoxRenderer';
 import { StyleProp } from '../../StyleProvider';
@@ -523,6 +522,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     }
                 }
                 break;
+            case StyleProp.FillColor:
+                if (doc && this.Document._currentFrame !== undefined) {
+                    return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
+                }
         }
         return styleProp;
     };
@@ -1216,7 +1219,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         // if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
         //     SelectionManager.DeselectAll();
         // }
-        if (this.props.getScrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined || doc.type === DocumentType.MARKER) {
+        if (this.props.getScrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined) {
             this.props.focus(doc, options);
         } else {
             const xfToCollection = options?.docTransform ?? Transform.Identity();
@@ -1386,7 +1389,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 dontScaleFilter={this.props.dontScaleFilter}
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
                 pointerEvents={this.pointerEvents}
-                rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
+                //rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
                 //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
             />
         );
@@ -1425,17 +1428,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
         const { z, zIndex } = childDoc;
         const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber);
-        const { x, y, opacity } = layoutFrameNumber === undefined ? { x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() } : CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
+        const { x, y, _width, _height, opacity, _rotation } =
+            layoutFrameNumber === undefined
+                ? { _width: Cast(childDocLayout._width, 'number'), _height: Cast(childDocLayout._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() }
+                : CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
         return {
             x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
             y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
             z: Cast(z, 'number'),
+            rotation: Cast(_rotation, 'number'),
             color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
             backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.getClusterColor(childDoc, this.props, StyleProp.BackgroundColor),
             opacity: this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
             zIndex: Cast(zIndex, 'number'),
-            width: Cast(childDocLayout._width, 'number'),
-            height: Cast(childDocLayout._height, 'number'),
+            width: _width,
+            height: _height,
             transition: StrCast(childDocLayout.dataTransition),
             pair: params.pair,
             replica: '',
@@ -1556,6 +1563,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     newPos.x !== lastPos.x ||
                     newPos.y !== lastPos.y ||
                     newPos.z !== lastPos.z ||
+                    newPos.rotation !== lastPos.rotation ||
                     newPos.zIndex !== lastPos.zIndex ||
                     newPos.transition !== lastPos.transition
                 ) {
@@ -1592,37 +1600,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     @action
-    scrollFocus = (anchor: Doc, options: DocFocusOptions) => {
+    scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => {
         const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
         if (options.preview) {
             this._focusFilters = StrListCast(anchor.presPinDocFilters);
             this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters);
             return undefined;
         }
-        return PresBox.restoreTargetDocView(
-            this.props.DocumentView?.(), //
-            { pinDocLayout: BoolCast(anchor.presPinLayout) },
-            anchor,
-            focusSpeed,
-            {
-                dataview: anchor.presData ? true : false,
-                pannable: anchor.presPinData ? true : false,
-                viewType: anchor.presPinViewType ? true : false,
-                filters: anchor.presPinDocFilters || anchor.presPinDocRangeFilters ? true : false,
-            }
-        )
-            ? focusSpeed
-            : undefined;
+        return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined;
     }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
-    getAnchor = (addAsAnnotation: boolean) => {
-        if (this.props.Document.annotationOn) {
-            return this.rootDoc;
-        }
-
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
-        const anchor = Docs.Create.TextanchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), presTransition: 500, annotationOn: this.rootDoc });
-        PresBox.pinDocView(anchor, { pinData: { pannable: true, viewType: true, filters: true } }, this.rootDoc);
+        const anchor = Docs.Create.CollectionAnchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), unrendered: true, presTransition: 500, annotationOn: this.rootDoc });
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true, viewType: true, filters: true } }, this.rootDoc);
 
         if (addAsAnnotation) {
             if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1d59d3356..81dc3aafd 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -18,6 +18,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import './AudioBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
+import { PinProps, PresBox } from './trails';
 
 /**
  * AudioBox
@@ -136,20 +137,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return { la1, la2, linkTime };
     }
 
-    getAnchor = (addAsAnnotation: boolean) => {
-        return (
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+        const anchor =
             CollectionStackedTimeline.createAnchor(
                 this.rootDoc,
                 this.dataDoc,
                 this.annotationKey,
-                '_timecodeToShow' /* audioStart */,
-                '_timecodeToHide' /* audioEnd */,
                 this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
                 undefined,
                 undefined,
                 addAsAnnotation
-            ) || this.rootDoc
-        );
+            ) || this.rootDoc;
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
+        return anchor;
     };
 
     // updates timecode and shows it in timeline, follows links at time
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index ba510e1dc..9a32556f8 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,6 @@
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../../fields/Doc';
-import { InkField } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
@@ -20,11 +19,10 @@ import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import React = require('react');
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
-    dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined;
+    dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; rotation?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined;
     sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined;
     renderCutoffProvider: (doc: Doc) => boolean;
     zIndex?: number;
-    rotation: number;
     dataTransition?: string;
     replica: string;
     CollectionFreeFormView: CollectionFreeFormView;
@@ -46,7 +44,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         { key: 'panX' },
         { key: 'panY' },
     ]; // fields that are configured to be animatable using animation frames
-    public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames
+    public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
     public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
     @observable _animPos: number[] | undefined = undefined;
     @observable _contentView: DocumentView | undefined | null;
@@ -56,7 +54,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     get transform() {
-        return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Document.rotation, this.props.rotation)}deg)`;
+        return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Rot, this.Rot)}deg)`;
     }
     get X() {
         return this.dataProvider?.x ?? NumCast(this.Document.x);
@@ -67,6 +65,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     get ZInd() {
         return this.dataProvider?.zIndex ?? NumCast(this.Document.zIndex);
     }
+    get Rot() {
+        return this.dataProvider?.rotation ?? NumCast(this.Document._rotation);
+    }
     get Opacity() {
         return this.dataProvider?.opacity;
     }
@@ -84,13 +85,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
-        if (doc === this.layoutDoc)
+        if (doc === this.layoutDoc) {
             // prettier-ignore
             switch (property) {
                 case StyleProp.Opacity:         return this.Opacity; // only change the opacity for this specific document, not its children
                 case StyleProp.BackgroundColor: return this.BackgroundColor;
                 case StyleProp.Color:           return this.Color;
             }
+        }
         return this.props.styleProvider?.(doc, props, property);
     };
 
@@ -108,6 +110,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         }, {} as { [val: string]: Opt<string> });
     }
 
+    public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) {
+        const timecode = Math.round(time);
+        Object.keys(vals).forEach(val => {
+            const findexed = Cast(d[`${val}-indexed`], listSpec('string'), []).slice();
+            findexed[timecode] = vals[val] as any as string;
+            d[`${val}-indexed`] = new List<string>(findexed);
+        });
+    }
+
     public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
         const timecode = Math.round(time);
         Object.keys(vals).forEach(val => {
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 2432d4c37..5783d0e57 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -16,6 +16,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import React = require('react');
 import _ = require('lodash');
+import { PinProps } from './trails';
 
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
@@ -175,7 +176,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     };
 
     public static finishLinkClick = undoBatch(
-        action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView) => {
+        action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) => {
             if (startLink === endLink) {
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
@@ -183,7 +184,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 DocumentLinksButton.AnnotationUri = undefined;
                 //!this.props.StartLink
             } else if (startLink !== endLink) {
-                endLink = endLinkView?.docView?._componentView?.getAnchor?.(true) || endLink;
+                endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
                 startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
                 const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined, undefined, undefined, true);
 
@@ -283,7 +284,18 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                         className={'documentLinksButton-endLink'}
                         ref={this._linkButton}
                         onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
-                        onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}>
+                        onClick={e =>
+                            DocumentLinksButton.StartLink &&
+                            DocumentLinksButton.finishLinkClick(
+                                e.clientX,
+                                e.clientY,
+                                DocumentLinksButton.StartLink,
+                                this.props.View.props.Document,
+                                true,
+                                this.props.View,
+                                (e.shiftKey ? { pinDocLayout: true, pinDocContent: true, pinData: { poslayoutview: true, dataannos: true, dataview: true } } : {}) as PinProps
+                            )
+                        }>
                         <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                     </div>
                 ) : null}
@@ -292,7 +304,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     }
 
     render() {
-        const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
+        const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link (shift key captures viewing state)';
         const buttonTitle = 'Tap to view links; double tap to open link collection';
         const title = this.props.ShowCount ? buttonTitle : menuTitle;
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9d966cdb2..ff6fe3712 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -54,7 +54,7 @@ import { LinkDocPreview } from './LinkDocPreview';
 import { RadialMenu } from './RadialMenu';
 import { ScriptingBox } from './ScriptingBox';
 import { PresEffect, PresEffectDirection } from './trails';
-import { PinProps } from './trails/PresBox';
+import { PinProps, PresBox } from './trails/PresBox';
 import React = require('react');
 import { GestureOverlay } from '../GestureOverlay';
 const { Howl } = require('howler');
@@ -121,8 +121,9 @@ export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void;
 export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
 export interface DocComponentView {
     updateIcon?: () => void; // updates the icon representation of the document
-    getAnchor?: (addAsAnnotation: boolean) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
-    scrollFocus?: (doc: Doc, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
+    getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
+    scrollPreview?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
+    scrollFocus?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
     brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void;
     reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void; // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
@@ -585,7 +586,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         LightboxView.SetCookie(StrCast(anchor['cookies-set']));
 
         // Restore viewing specification of target by reading them out of the anchor and applying to the target doc.
-        const focusSpeed = this._componentView?.scrollFocus?.(anchor, { ...options, preview: LinkDocPreview.LinkInfo ? true : false });
+        const presItem = DocCast(options.originatingDoc); // if originating doc was a presItem, then anchor will be its proto.  use presItem instead
+        const targetMatch = Doc.AreProtosEqual(anchor, this.rootDoc) || (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) ? true : false;
+        const scrollFocus =
+            (LinkDocPreview.LinkInfo ? this._componentView?.scrollPreview : undefined) ??
+            this._componentView?.scrollFocus ??
+            ((docView: DocumentView, anchor: Doc, options: DocFocusOptions) => (focusSpeed => (PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined))(options.instant ? 0 : options.zoomTime ?? 500));
+        const focusSpeed = targetMatch && scrollFocus?.(this.props.DocumentView(), presItem?.proto === anchor ? presItem : anchor, options);
 
         // FOCUS: navigate through the display hierarchy making sure the target is in view
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing;
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 5c0005dae..8fa01c97a 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -13,7 +13,7 @@ import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ViewBoxAnnotatableComponent } from '../DocComponent';
-import { DocFocusOptions } from './DocumentView';
+import { DocFocusOptions, DocumentView } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 
 const EquationSchema = createSchema({});
@@ -42,14 +42,14 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
         );
     }
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc });
+        const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true });
         anchor.xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
         anchor.yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
         if (addAsAnnotation) this.addDocument(anchor);
         return anchor;
     };
     @action
-    scrollFocus = (doc: Doc, smooth: DocFocusOptions) => {
+    scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => {
         this.dataDoc.xRange = new List<number>(Array.from(Cast(doc.xRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]))));
         this.dataDoc.yRange = new List<number>(Array.from(Cast(doc.yRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]))));
         return 0;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 0ba576e55..363cd1d94 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -8,7 +8,7 @@ import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { createSchema } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { DashColor, emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -26,11 +26,11 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, OpenWhere } from './DocumentView';
+import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
 import { FaceRectangles } from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import './ImageBox.scss';
-import { PresBox } from './trails';
+import { PinProps, PresBox } from './trails';
 import React = require('react');
 import Color = require('color');
 import { LinkDocPreview } from './LinkDocPreview';
@@ -71,34 +71,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
     };
 
-    @action
-    scrollFocus = (anchor: Doc, options: DocFocusOptions) => {
-        const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
-        return PresBox.restoreTargetDocView(
-            this.props.DocumentView?.(), //
-            { pinDocLayout: BoolCast(anchor.presPinLayout) },
-            anchor,
-            focusSpeed,
-            !anchor.presPinData
-                ? {}
-                : {
-                      pannable: true,
-                      dataannos: anchor.presAnnotations !== undefined,
-                      dataview: true,
-                  }
-        )
-            ? focusSpeed
-            : undefined;
-    }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-
-    getAnchor = (addAsAnnotation: boolean) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor =
             this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
             Docs.Create.ImageanchorDocument({ title: 'ImgAnchor:' + this.rootDoc.title, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc });
         if (anchor) {
             if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
             /* addAsAnnotation &&*/ this.addDocument(anchor);
-            PresBox.pinDocView(anchor, { pinData: { pannable: true } }, this.rootDoc);
+            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true } }, this.rootDoc);
             return anchor;
         }
         return this.rootDoc;
@@ -177,9 +157,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
         const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
         this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh;
-        this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nw;
+        this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nh;
         this.rootDoc._panX = nh * NumCast(this.rootDoc._panX);
-        this.rootDoc._panY = nw * NumCast(this.rootDoc._panY);
+        this.rootDoc._panY = nh * NumCast(this.rootDoc._panY);
         this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined;
         this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined;
         this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index a2143f629..33fab6873 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -36,8 +36,6 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
         this._timeout && clearTimeout(this._timeout);
     }
 
-    getAnchor = (addAsAnnotation: boolean) => this.rootDoc;
-
     getTitle() {
         return this.rootDoc['title-custom'] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
     }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 88d134bba..8d7ab2f0d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -5,7 +5,7 @@ import * as Pdfjs from 'pdfjs-dist';
 import 'pdfjs-dist/web/pdf_viewer.css';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField, PdfField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -22,13 +22,13 @@ import { LightboxView } from '../LightboxView';
 import { CreateImage } from '../nodes/WebBoxRenderer';
 import { PDFViewer } from '../pdf/PDFViewer';
 import { SidebarAnnos } from '../SidebarAnnos';
+import { DocFocusOptions, DocumentView } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { ImageBox } from './ImageBox';
 import './PDFBox.scss';
+import { PinProps, PresBox } from './trails';
 import { VideoBox } from './VideoBox';
 import React = require('react');
-import { PresBox } from './trails';
-import { DocFocusOptions } from './DocumentView';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -200,18 +200,18 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view);
 
-    scrollFocus = (doc: Doc, options: DocFocusOptions) => {
+    scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => {
         let didToggle = false;
-        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) {
             this.toggleSidebar(options.preview);
             didToggle = true;
         }
-        if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
-        this._initialScrollTarget = doc;
-        PresBox.restoreTargetDocView(this.props.DocumentView?.(), {}, doc, options.zoomTime ?? 500, { pannable: doc.presPinData ? true : false });
-        return this._pdfViewer?.scrollFocus(doc, NumCast(doc.presPinViewScroll, NumCast(doc.y)), options) ?? (didToggle ? 1 : undefined);
+        if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1;
+        this._initialScrollTarget = anchor;
+        PresBox.restoreTargetDocView(docView, anchor, options.zoomTime ?? 500);
+        return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.presPinViewScroll, NumCast(anchor.y)), options) ?? (didToggle ? 1 : undefined);
     };
-    getAnchor = (addAsAnnotation: boolean) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         let ele: Opt<HTMLDivElement> = undefined;
         if (this._pdfViewer?.selectionContent()) {
             ele = document.createElement('div');
@@ -223,11 +223,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
-            PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
             return anchor;
         };
         const annoAnchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations(), true);
         const anchor = annoAnchor ?? docAnchor();
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
         anchor.textHtml = ele?.innerHTML;
         if (addAsAnnotation || annoAnchor) {
@@ -287,8 +287,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     setPdfViewer = (pdfViewer: PDFViewer) => {
         this._pdfViewer = pdfViewer;
-        if (this._initialScrollTarget) {
-            this.scrollFocus(this._initialScrollTarget, { instant: true });
+        const docView = this.props.DocumentView?.();
+        if (this._initialScrollTarget && docView) {
+            this.scrollFocus(docView, this._initialScrollTarget, { instant: true });
             this._initialScrollTarget = undefined;
         }
     };
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index f94996c66..61e4894f0 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -130,7 +130,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     }
     getAnchor = (addAsAnnotation: boolean) => {
         const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
     };
 
     videoLoad = () => {
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 281967a21..fa2021642 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -20,6 +20,7 @@ import { EditableView } from '../EditableView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { OverlayView } from '../OverlayView';
 import { DocumentIconContainer } from './DocumentIcon';
+import { DocFocusOptions, DocumentView } from './DocumentView';
 import './ScriptingBox.scss';
 const _global = (window /* browser */ || global) /* node */ as any;
 
@@ -481,10 +482,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         return value;
     }
 
-    scrollFocus = () => {
-        return undefined;
-    };
-
     getSuggestedParams(pos: number) {
         const firstScript = this.rawText.slice(0, pos);
         const indexP = firstScript.lastIndexOf('.');
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 1dfa55c64..c26562e7c 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -7,6 +7,7 @@ import * as rp from 'request-promise';
 import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { AudioField, ImageField, VideoField } from '../../../fields/URLField';
 import { emptyFunction, formatTime, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -27,11 +28,11 @@ import { DocumentDecorations } from '../DocumentDecorations';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
 import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
+import { OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { RecordingBox } from './RecordingBox';
+import { PinProps, PresBox } from './trails';
 import './VideoBox.scss';
-import { ObjectField } from '../../../fields/ObjectField';
-import { DocFocusOptions, OpenWhere } from './DocumentView';
 const path = require('path');
 
 /**
@@ -390,13 +391,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true));
     };
 
-    getAnchor = (addAsAnnotation: boolean) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
         const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
-        return (
-            CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) ||
-            this.rootDoc
-        );
+        if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
+        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc;
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
+        return anchor;
     };
 
     // sets video info on load
@@ -953,14 +954,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         );
     };
 
-    scrollFocus = (doc: Doc, options: DocFocusOptions) => {
-        if (doc !== this.rootDoc) {
-            const showTime = Cast(doc._timecodeToShow, 'number', null);
-            showTime !== undefined && setTimeout(() => this.Seek(showTime), 100);
-            return 0.1;
-        }
-    };
-
     // renders CollectionStackedTimeline
     @computed get renderTimeline() {
         return (
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 4c9bead9d..caab18e0d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
@@ -11,8 +11,9 @@ import { listSpec } from '../../../fields/Schema';
 import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, getWordAtPoint, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
+import { DragManager } from '../../util/DragManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
@@ -21,7 +22,6 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
-import { DocumentDecorations } from '../DocumentDecorations';
 import { Colors } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
@@ -29,13 +29,13 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
 import { Annotation } from '../pdf/Annotation';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, DocumentView, DocumentViewInternal, DocumentViewProps } from './DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { LinkDocPreview } from './LinkDocPreview';
 import { VideoBox } from './VideoBox';
 import './WebBox.scss';
 import React = require('react');
-import { DragManager } from '../../util/DragManager';
+import { PinProps, PresBox } from './trails';
 const { CreateImage } = require('./WebBoxRenderer');
 const _global = (window /* browser */ || global) /* node */ as any;
 const htmlToText = require('html-to-text');
@@ -189,6 +189,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const reqdFuncs: { [key: string]: string } = {};
             // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
             reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`;
+            reqdFuncs[this.fieldKey + '-annotations-setter'] = `this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"] = value`;
             reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`;
             DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
@@ -293,27 +294,35 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => (this._setBrushViewer = func);
     brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view);
-    scrollFocus = (doc: Doc, options: DocFocusOptions) => {
-        if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), options.preview);
-        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+    focus = (doc: Doc, options: DocFocusOptions) => {
+        !doc.unrendered && this.props.DocumentView?.() && this.scrollFocus(this.props.DocumentView?.(), doc, {});
+        this.props.focus(doc, options);
+    };
+    scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => {
+        if (this._url && StrCast(anchor.webUrl) !== this._url) this.submitURL(StrCast(anchor.webUrl), options.preview);
+        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) {
             this.toggleSidebar(options.preview);
         }
-        if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
-        if (doc !== this.rootDoc && this._outerRef.current) {
+        if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1;
+        if (anchor !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this._scrollHeight));
+            const scrollTo = !anchor[HeightSym]()
+                ? NumCast(anchor.y)
+                : Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight));
+            const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
             if (scrollTo !== undefined && this._initialScroll === undefined) {
-                const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500;
                 this.goTo(scrollTo, focusSpeed, options.easeFunc);
+                PresBox.restoreTargetDocView(docView, anchor, focusSpeed);
                 return focusSpeed;
             } else if (!this._webPageHasBeenRendered || !this._scrollHeight || this._initialScroll !== undefined) {
                 this._initialScroll = scrollTo;
+                return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined;
             }
         }
         return undefined;
     };
 
-    getAnchor = (addAsAnnotation: boolean) => {
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         let ele: Opt<HTMLDivElement> = undefined;
         try {
             const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents();
@@ -330,9 +339,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
         anchor.text = ele?.textContent ?? '';
         anchor.textHtml = ele?.innerHTML;
-        addAsAnnotation && this.addDocumentWrapper(anchor);
+        //addAsAnnotation &&
+        this.addDocumentWrapper(anchor);
         return anchor;
     };
 
@@ -901,6 +912,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 PanelHeight={this.panelHeight}
                 ScreenToLocalTransform={this.scrollXf}
                 NativeDimScaling={returnOne}
+                focus={this.focus}
                 dropAction={'alias'}
                 docFilters={docFilters}
                 select={emptyFunction}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 93761633c..0468d1c47 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -27,6 +27,7 @@ import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetAc
 import { InkTranscription } from '../../InkTranscription';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
+import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
 import { OpenWhere } from '../DocumentView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
 import { WebBox } from '../WebBox';
@@ -551,11 +552,30 @@ ScriptingGlobals.add(function setView(view: string) {
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement()?.props.Document ?? LinkManager.currentLink;
-    if (checkResult) {
-        return selected?._backgroundColor ?? 'transparent';
+    const selectedViews = SelectionManager.Views();
+    if (selectedViews.length) {
+        if (checkResult) {
+            const selView = selectedViews.lastElement();
+            const layoutFrameNumber = Cast(selView.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+            return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber).backgroundColor ?? 'transparent';
+        }
+        selectedViews.forEach(dv => {
+            const layoutFrameNumber = Cast(dv.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+            if (contentFrameNumber !== undefined) {
+                CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { backgroundColor: color });
+            } else {
+                dv.rootDoc._backgroundColor = color;
+            }
+        });
+    } else {
+        const selected = SelectionManager.Docs().length ? SelectionManager.Docs() : LinkManager.currentLink ? [LinkManager.currentLink] : [];
+        if (checkResult) {
+            return selected.lastElement()?._backgroundColor ?? 'transparent';
+        }
+        selected.forEach(doc => (doc._backgroundColor = color));
     }
-    if (selected) selected._backgroundColor = color;
 });
 
 // toggle: Set overlay status of selected document
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 788de7af9..269438a7c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -45,7 +45,7 @@ import { LightboxView } from '../../LightboxView';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { StyleProp } from '../../StyleProvider';
-import { DocFocusOptions, DocumentViewInternal, OpenWhere } from '../DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
 import { LinkDocPreview } from '../LinkDocPreview';
 import { DashDocCommentView } from './DashDocCommentView';
@@ -932,7 +932,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return anchorDoc ?? this.rootDoc;
     }
 
-    scrollFocus = (textAnchor: Doc, options: DocFocusOptions) => {
+    scrollFocus = (docView: DocumentView, textAnchor: Doc, options: DocFocusOptions) => {
         let didToggle = false;
         if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) {
             this.toggleSidebar(options.preview);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 13cbd87eb..45d386436 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -4,9 +4,9 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { AnimationSym, Doc, DocListCast, FieldResult, Opt, StrListCast } from '../../../../fields/Doc';
+import { AnimationSym, Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../../fields/Doc';
 import { Copy, Id } from '../../../../fields/FieldSymbols';
-import { InkTool } from '../../../../fields/InkField';
+import { InkField, InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
 import { ObjectField } from '../../../../fields/ObjectField';
 import { listSpec } from '../../../../fields/Schema';
@@ -34,8 +34,23 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { ScriptingBox } from '../ScriptingBox';
 import './PresBox.scss';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
+import { SerializationHelper } from '../../../util/SerializationHelper';
 const { Howl } = require('howler');
 
+export interface pinDataTypes {
+    scrollable?: boolean;
+    pannable?: boolean;
+    viewType?: boolean;
+    inkable?: boolean;
+    filters?: boolean;
+    pivot?: boolean;
+    temporal?: boolean;
+    clippable?: boolean;
+    dataview?: boolean;
+    textview?: boolean;
+    poslayoutview?: boolean;
+    dataannos?: boolean;
+}
 export interface PinProps {
     audioRange?: boolean;
     activeFrame?: number;
@@ -45,18 +60,7 @@ export interface PinProps {
     pinDocLayout?: boolean; // pin layout info (width/height/x/y)
     pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text)
     pinAudioPlay?: boolean; // pin audio annotation
-    pinData?: {
-        scrollable?: boolean | undefined;
-        pannable?: boolean | undefined;
-        viewType?: boolean | undefined;
-        filters?: boolean | undefined;
-        temporal?: boolean | undefined;
-        clippable?: boolean | undefined;
-        dataview?: boolean | undefined;
-        textview?: boolean | undefined;
-        poslayoutview?: boolean | undefined;
-        dataannos?: boolean | undefined;
-    };
+    pinData?: pinDataTypes;
 }
 
 @observer
@@ -318,19 +322,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.onHideDocument(); //Handles hide after/before
         }
     });
-    static pinDataTypes(target?: Doc): {
-        scrollable?: boolean;
-        pannable?: boolean;
-        viewType?: boolean;
-        filters?: boolean;
-        temporal?: boolean;
-        clippable?: boolean;
-        dataview?: boolean;
-        textview?: boolean;
-        poslayoutview?: boolean;
-        dataannos?: boolean;
-    } {
+    static pinDataTypes(target?: Doc): pinDataTypes {
         const targetType = target?.type as any;
+        const inkable = [DocumentType.INK].includes(targetType);
         const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._viewType === CollectionViewType.Stacking;
         const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform);
         const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType);
@@ -340,18 +334,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const textview = [DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined;
         const viewType = targetType === DocumentType.COL;
         const filters = true;
+        const pivot = true;
         const dataannos = false;
-        return { scrollable, pannable, viewType, filters, temporal, clippable, dataview, textview, poslayoutview, dataannos };
+        return { scrollable, pannable, inkable, viewType, pivot, filters, temporal, clippable, dataview, textview, poslayoutview, dataannos };
     }
 
     @action
     playAnnotation = (anno: AudioField) => {};
     @action
-    static restoreTargetDocView(bestTargetView: Opt<DocumentView>, pinProps: PinProps | undefined, activeItem: Doc, transTime: number, pinDataTypes = this.pinDataTypes(bestTargetView?.rootDoc)) {
-        if (!bestTargetView) return;
-        const bestTarget = bestTargetView.rootDoc;
+    static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.presPinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) {
+        const bestTarget = bestTargetView?.rootDoc ?? (targetDoc?.unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
+        if (!bestTarget) return;
         let changed = false;
-        if (pinProps?.pinDocLayout) {
+        if (pinDocLayout) {
             if (
                 bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) ||
                 bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) ||
@@ -369,33 +364,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 changed = true;
             }
         }
-        if (pinDataTypes.clippable) {
+        if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presPinClipWidth !== undefined)) {
             if (bestTarget._clipWidth !== activeItem.presPinClipWidth) {
                 bestTarget._clipWidth = activeItem.presPinClipWidth;
                 changed = true;
             }
         }
-        if (pinDataTypes.temporal) {
+        if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
             if (bestTarget._currentTimecode !== activeItem.presStartTime) {
                 bestTarget._currentTimecode = activeItem.presStartTime;
                 changed = true;
             }
         }
-        if (pinDataTypes.viewType && activeItem.presPinViewType !== undefined) {
+        if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.presFillColor !== undefined || activeItem.color !== undefined))) {
+            if (bestTarget.fillColor !== activeItem.presFillColor) {
+                Doc.GetProto(bestTarget).fillColor = activeItem.presFillColor;
+                changed = true;
+            }
+            if (bestTarget.color !== activeItem.color) {
+                Doc.GetProto(bestTarget).color = activeItem.color;
+                changed = true;
+            }
+        }
+        if ((pinDataTypes?.viewType && activeItem.presPinViewType !== undefined) || (!pinDataTypes && activeItem.presPinViewType !== undefined)) {
             if (bestTarget._viewType !== activeItem.presPinViewType) {
                 bestTarget._viewType = activeItem.presPinViewType;
                 changed = true;
             }
         }
 
-        if (pinDataTypes.filters && activeItem.presPinDocFilters !== undefined) {
+        if ((pinDataTypes?.filters && activeItem.presPinDocFilters !== undefined) || (!pinDataTypes && activeItem.presPinDocFilters !== undefined)) {
             if (bestTarget.docFilters !== activeItem.presPinDocFilters) {
                 bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presPinDocFilters as ObjectField) || new List<string>([]);
                 changed = true;
             }
         }
 
-        if (pinDataTypes.scrollable) {
+        if ((pinDataTypes?.pivot && activeItem.presPinPvitoField !== undefined) || (!pinDataTypes && activeItem.presPinPivotField !== undefined)) {
+            if (bestTarget.pivotField !== activeItem.presPinPivotField) {
+                bestTarget.pivotField = activeItem.presPinPivotField;
+                bestTarget._prevFilterIndex = 1; // need to revisit this...see CollectionTimeView
+                changed = true;
+            }
+        }
+
+        if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presPinViewScroll !== undefined)) {
             if (bestTarget._scrollTop !== activeItem.presPinViewScroll) {
                 bestTarget._scrollTop = activeItem.presPinViewScroll;
                 changed = true;
@@ -406,39 +419,59 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }
         }
-        if (pinDataTypes.dataannos) {
+        if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.presAnnotations !== undefined)) {
             const fkey = Doc.LayoutFieldKey(bestTarget);
-            Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]);
+            const oldItems = DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered);
+            const newItems = DocListCast(activeItem.presAnnotations).map(doc => {
+                doc.hidden = false;
+                return doc;
+            });
+            const hiddenItems = DocListCast(bestTarget[fkey + '-annotations'])
+                .filter(doc => !doc.unrendered && !newItems.includes(doc))
+                .map(doc => {
+                    doc.hidden = true;
+                    return doc;
+                });
+            const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
+            Doc.GetProto(bestTarget)[fkey + '-annotations'] = newList;
         }
-        if (pinDataTypes.dataview && activeItem.presData !== undefined) {
+        if ((pinDataTypes?.dataview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
             bestTarget._dataTransition = `all ${transTime}ms`;
             const fkey = Doc.LayoutFieldKey(bestTarget);
             Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
             bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt;
             setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
         }
-        if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
-        if (pinDataTypes.poslayoutview) {
+        if ((pinDataTypes?.textview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) {
+            Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+        }
+        if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) {
             changed = true;
+            const layoutField = Doc.LayoutFieldKey(bestTarget);
+            const transitioned = new Set<Doc>();
             StrListCast(activeItem.presPinLayoutData)
-                .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
-                .forEach(data => {
-                    const doc = DocServer.GetCachedRefField(data.id) as Doc;
-                    doc._dataTransition = `all ${transTime}ms`;
-                    doc.x = data.x;
-                    doc.y = data.y;
-                    doc._width = data.w;
-                    doc._height = data.h;
+                .map(str => JSON.parse(str) as { id: string; x: number; y: number; back: string; fill: string; w: number; h: number; data: string; text: string })
+                .forEach(async data => {
+                    const doc = DocCast(DocServer.GetCachedRefField(data.id));
+                    if (doc) {
+                        transitioned.add(doc);
+                        const field = !data.data ? undefined : await SerializationHelper.Deserialize(data.data);
+                        const tfield = !data.text ? undefined : await SerializationHelper.Deserialize(data.text);
+                        doc._dataTransition = `all ${transTime}ms`;
+                        doc.x = data.x;
+                        doc.y = data.y;
+                        data.back && (doc._backgroundColor = data.back);
+                        data.fill && (doc._fillColor = data.fill);
+                        doc._width = data.w;
+                        doc._height = data.h;
+                        data.data && (Doc.GetProto(doc).data = field);
+                        data.text && (Doc.GetProto(doc).text = tfield);
+                        Doc.AddDocToList(Doc.GetProto(bestTarget), layoutField, doc);
+                    }
                 });
-            setTimeout(
-                () =>
-                    StrListCast(activeItem.presPinLayoutData)
-                        .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number })
-                        .forEach(action(data => ((DocServer.GetCachedRefField(data.id) as Doc)._dataTransition = undefined))),
-                transTime + 10
-            );
+            setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10);
         }
-        if (pinDataTypes.pannable) {
+        if (pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPinViewX !== undefined || activeItem.presPinViewScale !== undefined))) {
             const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
             if (contentBounds) {
                 const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
@@ -460,7 +493,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         if (changed) {
-            return bestTargetView.setViewTransition('all', transTime);
+            return bestTargetView?.setViewTransition('all', transTime);
         }
     }
 
@@ -499,12 +532,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered));
             }
             if (pinProps.pinData.textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text;
+            if (pinProps.pinData.inkable) {
+                pinDoc.presFillColor = targetDoc.fillColor;
+                pinDoc.presColor = targetDoc.color;
+            }
             if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop;
             if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth;
             if (pinProps.pinData.poslayoutview)
-                pinDoc.presPinLayoutData = new List<string>(DocListCast(targetDoc[fkey] as ObjectField).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) })));
+                pinDoc.presPinLayoutData = new List<string>(
+                    DocListCast(targetDoc[fkey] as ObjectField).map(d =>
+                        JSON.stringify({
+                            id: d[Id],
+                            x: NumCast(d.x),
+                            y: NumCast(d.y),
+                            w: NumCast(d._width),
+                            h: NumCast(d._height),
+                            fill: StrCast(d._fillColor),
+                            back: StrCast(d._backgroundColor),
+                            data: SerializationHelper.Serialize(d.data instanceof ObjectField ? d.data[Copy]() : ''),
+                            text: SerializationHelper.Serialize(d.text instanceof ObjectField ? d.text[Copy]() : ''),
+                        })
+                    )
+                );
             if (pinProps.pinData.viewType) pinDoc.presPinViewType = targetDoc._viewType;
             if (pinProps.pinData.filters) pinDoc.presPinDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField);
+            if (pinProps.pinData.pivot) pinDoc.presPinPivotField = targetDoc._pivotField;
             if (pinProps.pinData.pannable) {
                 pinDoc.presPinViewX = NumCast(targetDoc._panX);
                 pinDoc.presPinViewY = NumCast(targetDoc._panY);
@@ -586,7 +638,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined;
             if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) {
                 // targetDoc may or may not be displayed.  so get the first available document (or alias) view that matches targetDoc and use it
-                PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500));
+                // PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500), undefined, targetDoc);
             }
         };
         const finishAndRestoreLayout = () => {
@@ -660,16 +712,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     _exitTrail: Opt<() => void>;
     PlayTrail = (docs: Doc[]) => {
-        const savedStates = docs.map(doc => (doc._viewType !== CollectionViewType.Freeform ? undefined : { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) }));
+        const savedStates = docs.map(doc => {
+            switch (doc.type) {
+                case DocumentType.COL:
+                    if (doc._viewType === CollectionViewType.Freeform) return { type: CollectionViewType.Freeform, doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) };
+                    break;
+                case DocumentType.INK:
+                    if (doc.data instanceof InkField) {
+                        return { type: doc.type, doc, data: doc.data?.[Copy](), fillColor: doc.fillColor, color: doc.color, x: NumCast(doc.x), y: NumCast(doc.y) };
+                    }
+            }
+            return undefined;
+        });
         this.startPresentation(0);
         this._exitTrail = () => {
             savedStates
                 .filter(savedState => savedState)
                 .map(savedState => {
-                    const { x, y, s, c } = savedState!;
-                    c._panX = x;
-                    c._panY = y;
-                    c._viewScale = s;
+                    switch (savedState?.type) {
+                        case CollectionViewType.Freeform:
+                            {
+                                const { x, y, s, doc } = savedState!;
+                                doc._panX = x;
+                                doc._panY = y;
+                                doc._viewScale = s;
+                            }
+                            break;
+                        case DocumentType.INK:
+                            {
+                                const { data, fillColor, color, x, y, doc } = savedState!;
+                                doc.x = x;
+                                doc.y = y;
+                                doc.data = data;
+                                doc.fillColor = fillColor;
+                                doc.color = color;
+                            }
+                            break;
+                    }
                 });
             LightboxView.SetLightboxDoc(undefined);
             Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
@@ -1869,7 +1948,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         );
     }
 
-    scrollFocus = () => {
+    scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => {
         // this.gotoDocument(0);
         // this.startOrPause(false);
         return undefined;
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 4a7f5d038..fc8f1da49 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -41,7 +41,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';
     @observable private _showLinkPopup: boolean = false;
 
-    @observable public Highlighting: boolean = false;
     @observable public Status: 'marquee' | 'annotation' | '' = '';
 
     public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
@@ -121,9 +120,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     @action
     highlightClicked = (e: React.MouseEvent) => {
-        if (!this.Highlight(this.highlightColor, false, undefined, true) && this.Pinned) {
-            this.Highlighting = !this.Highlighting;
-        }
+        this.Highlight(this.highlightColor, false, undefined, true);
         AnchorMenu.Instance.fadeOut(true);
     };
 
@@ -138,7 +135,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         const button = (
             <button className="antimodeMenu-button anchor-color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
                 <div className="anchor-color-preview">
-                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: 'transform 0.1s', transform: this.Highlighting ? '' : 'rotate(-45deg)' }} />
+                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />
                     <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
                 </div>
             </button>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 2db44788f..9877690f8 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -30,7 +30,7 @@ const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
 // The workerSrc property shall be specified.
-pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js';
+pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.16.105/build/pdf.worker.js';
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
@@ -179,7 +179,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, this._scrollHeight);
+            const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc };
                 else if (!options.instant && !options.preview) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper);
@@ -518,6 +518,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return this.props.styleProvider?.(doc, props, property);
     };
 
+    focus = (doc: Doc, options: DocFocusOptions) => {
+        !doc.unrendered && this.props.DocumentView?.() && this.scrollToAnnotation(doc);
+        this.props.focus(doc, options);
+    };
     renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => (
         <div
             className="pdfViewerDash-overlay"
@@ -540,6 +544,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 PanelWidth={this.panelWidth}
                 ScreenToLocalTransform={this.overlayTransform}
                 dropAction={'alias'}
+                focus={this.focus}
                 docFilters={docFilters}
                 select={emptyFunction}
                 bringToFront={emptyFunction}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index df49c32f0..ed5eaa756 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -722,13 +722,22 @@ export namespace Doc {
         return bestAlias ?? Doc.MakeAlias(doc);
     }
 
-    export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<Doc, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise<Doc> {
+    export async function makeClone(
+        doc: Doc,
+        cloneMap: Map<string, Doc>,
+        linkMap: Map<Doc, Doc>,
+        rtfs: { copy: Doc; key: string; field: RichTextField }[],
+        exclusions: string[],
+        topLevelExclusions: string[],
+        dontCreate: boolean,
+        asBranch: boolean
+    ): Promise<Doc> {
         if (Doc.IsBaseProto(doc)) return doc;
         if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
         const copy = dontCreate ? (asBranch ? Cast(doc.branchMaster, Doc, null) || doc : doc) : new Doc(undefined, true);
         cloneMap.set(doc[Id], copy);
         const fieldExclusions = doc.type === DocumentType.MARKER ? exclusions.filter(ex => ex !== 'annotationOn') : exclusions;
-        const filter = [...fieldExclusions, ...Cast(doc.cloneFieldFilter, listSpec('string'), [])];
+        const filter = [...fieldExclusions, ...topLevelExclusions, ...Cast(doc.cloneFieldFilter, listSpec('string'), [])];
         await Promise.all(
             Object.keys(doc).map(async key => {
                 if (filter.includes(key)) return;
@@ -739,10 +748,10 @@ export namespace Doc {
                     const list = await Cast(doc[key], listSpec(Doc));
                     const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
                     if (docs !== undefined && docs.length) {
-                        const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)));
+                        const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch)));
                         !dontCreate && assignKey(new List<Doc>(clones));
                     } else if (doc[key] instanceof Doc) {
-                        assignKey(key.includes('layout[') ? undefined : key.startsWith('layout') ? (doc[key] as Doc) : await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields
+                        assignKey(key.includes('layout[') ? undefined : key.startsWith('layout') ? (doc[key] as Doc) : await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields
                     } else {
                         !dontCreate && assignKey(ObjectField.MakeCopy(field));
                         if (field instanceof RichTextField) {
@@ -754,17 +763,18 @@ export namespace Doc {
                 };
                 if (key === 'proto') {
                     if (doc[key] instanceof Doc) {
-                        assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch));
+                        assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch));
                     }
                 } else if (key === 'anchor1' || key === 'anchor2') {
                     if (doc[key] instanceof Doc) {
-                        assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, true, asBranch));
+                        assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, [], true, asBranch));
                     }
                 } else {
                     if (field instanceof RefField) {
                         assignKey(field);
                     } else if (cfield instanceof ComputedField) {
-                        !dontCreate && assignKey(ComputedField.MakeFunction(cfield.script.originalScript));
+                        !dontCreate && assignKey(cfield[Copy]());
+                        //   ComputedField.MakeFunction(cfield.script.originalScript));
                     } else if (field instanceof ObjectField) {
                         await copyObjectField(field);
                     } else if (field instanceof Promise) {
@@ -776,7 +786,7 @@ export namespace Doc {
             })
         );
         for (const link of Array.from(doc[DirectLinksSym])) {
-            const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch);
+            const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch);
             linkMap.set(link, linkClone);
         }
         if (!dontCreate) {
@@ -793,7 +803,7 @@ export namespace Doc {
     export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false, cloneMap: Map<string, Doc> = new Map()) {
         const linkMap = new Map<Doc, Doc>();
         const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
-        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf', 'branches', 'branchOf', 'context'], dontCreate, asBranch);
+        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf', 'branches', 'branchOf'], ['context'], dontCreate, asBranch);
         Array.from(linkMap.entries()).map((links: Doc[]) => LinkManager.Instance.addLink(links[1], true));
         rtfMap.map(({ copy, key, field }) => {
             const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index b23732b45..5f02bd73d 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -185,9 +185,11 @@ export class ComputedField extends ScriptField {
         const compiled = ScriptField.CompileScript(script, params, false);
         return compiled.compiled ? new ComputedField(compiled) : undefined;
     }
-    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
+    public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) {
         const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
-        return compiled.compiled ? new ComputedField(compiled) : undefined;
+        const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined;
+        const compiledsetscript = compiledsetter?.compiled ? compiledsetter : undefined;
+        return compiled.compiled ? new ComputedField(compiled, compiledsetscript) : undefined;
     }
     public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt<number>) {
         if (!doc[`${fieldKey}-indexed`]) {
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 33809824f..f461cf3fa 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -501,7 +501,7 @@ export namespace DashUploadUtils {
 
     const parseExifData = async (source: string) => {
         const image = await request.get(source, { encoding: null });
-        const { data, error } = await new Promise(resolve => {
+        const { data, error } = await new Promise<{ data: any; error: any }>(resolve => {
             new ExifImage({ image }, (error, data) => {
                 let reason: Opt<string> = undefined;
                 if (error) {
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 9b91a35a6..68b003496 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -199,7 +199,7 @@ export namespace WebSocket {
         return Database.Instance.getDocument(id, callback);
     }
     function GetRefField([id, callback]: [string, (result?: Transferable) => void]) {
-        process.stdout.write(`.`);
+        process.stdout.write(`+`);
         GetRefFieldLocal([id, callback]);
     }
 
-- 
cgit v1.2.3-70-g09d2


From f1dea8f31886c1e5e3d8bb772434009803b8fb8a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 28 Feb 2023 14:47:27 -0500
Subject: added view spec capture options to complete link

---
 src/client/views/DocumentButtonBar.scss            | 19 +++---
 src/client/views/DocumentButtonBar.tsx             | 70 ++++++++++++++++++++--
 src/client/views/nodes/DocumentLinksButton.tsx     | 63 ++-----------------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 15 ++++-
 4 files changed, 91 insertions(+), 76 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss
index 835d6c8bb..1abf33cac 100644
--- a/src/client/views/DocumentButtonBar.scss
+++ b/src/client/views/DocumentButtonBar.scss
@@ -18,6 +18,8 @@ $linkGap: 3px;
     cursor: pointer;
 }
 
+.documentButtonBar-endLinkTypes,
+.documentButtonBar-linkTypes,
 .documentButtonBar-followTypes,
 .documentButtonBar-pinTypes {
     position: absolute;
@@ -28,15 +30,6 @@ $linkGap: 3px;
     height: 20px;
     align-items: center;
 }
-.documentButtonBar-linkTypes {
-    position: absolute;
-    display: none;
-    width: 60px;
-    top: -14px;
-    background: black;
-    height: 20px;
-    align-items: center;
-}
 .documentButtonBar-followTypes {
     width: 20px;
     display: none;
@@ -72,6 +65,14 @@ $linkGap: 3px;
         }
     }
 }
+.documentButtonBar-endLink {
+    color: white;
+    &:hover {
+        .documentButtonBar-endLinkTypes {
+            display: flex;
+        }
+    }
+}
 
 .documentButtonBar-pinIcon {
     &:hover {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 9298c881c..20c35739e 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -28,6 +28,7 @@ import { TemplateMenu } from './TemplateMenu';
 import React = require('react');
 import { DocumentType } from '../documents/DocumentTypes';
 import { FontIconBox } from './nodes/button/FontIconBox';
+import { PinProps } from './nodes/trails';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -285,6 +286,52 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             </div>
         );
     }
+    @observable subEndLink = '';
+    @computed
+    get endLinkButton() {
+        const linkBtn = (pinDocLayout: boolean, pinDocContent: boolean, icon: IconProp) => {
+            const tooltip = `Finish Link and Save ${this.subEndLink} data`;
+            return !this.view0 ? null : (
+                <Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
+                    <div className="documentButtonBar-pinIcon">
+                        <FontAwesomeIcon
+                            className="documentdecorations-icon"
+                            style={{ width: 20 }}
+                            key={icon.toString()}
+                            size="sm"
+                            icon={icon}
+                            onPointerEnter={action(e => (this.subEndLink = (pinDocLayout ? 'Layout' : '') + (pinDocLayout && pinDocContent ? ' &' : '') + (pinDocContent ? ' Content' : '')))}
+                            onPointerLeave={action(e => (this.subEndLink = ''))}
+                            onClick={e => {
+                                const docs = this.props
+                                    .views()
+                                    .filter(v => v)
+                                    .map(dv => dv!.rootDoc);
+                                this.view0 &&
+                                    DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.view0.props.Document, true, this.view0, {
+                                        pinDocLayout,
+                                        pinDocContent,
+                                        pinData: !pinDocContent ? {} : { poslayoutview: true, dataannos: true, dataview: true },
+                                    } as PinProps);
+
+                                e.stopPropagation();
+                            }}
+                        />
+                    </div>
+                </Tooltip>
+            );
+        };
+        return !this.view0 ? null : (
+            <div className="documentButtonBar-icon documentButtonBar-pin">
+                <div className="documentButtonBar-pinTypes">
+                    {linkBtn(true, false, 'window-maximize')}
+                    {linkBtn(false, true, 'address-card')}
+                    {linkBtn(true, true, 'id-card')}
+                </div>
+                <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
+            </div>
+        );
+    }
 
     @observable subPin = '';
     @computed
@@ -497,6 +544,23 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         this._showLinkPopup = !this._showLinkPopup;
         e.stopPropagation();
     };
+
+    @observable _captureEndLinkLayout = false;
+    @action
+    captureEndLinkLayout = (e: React.PointerEvent) => {
+        this._captureEndLinkLayout = !this._captureEndLinkLayout;
+    };
+    @observable _captureEndLinkContent = false;
+    @action
+    captureEndLinkContent = (e: React.PointerEvent) => {
+        this._captureEndLinkContent = !this._captureEndLinkContent;
+    };
+
+    @action
+    captureEndLinkState = (e: React.PointerEvent) => {
+        this._captureEndLinkContent = this._captureEndLinkLayout = !this._captureEndLinkLayout;
+    };
+
     @action
     toggleTrail = (e: React.PointerEvent) => {
         const rootView = this.props.views()[0];
@@ -539,11 +603,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                     <div className="documentButtonBar-button">{this.linkButton}</div>
                 )}
 
-                {(DocumentLinksButton.StartLink || Doc.UserDoc()['documentLinksButton-fullMenu']) && DocumentLinksButton.StartLink !== doc ? (
-                    <div className="documentButtonBar-button">
-                        <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
-                    </div>
-                ) : null}
+                {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== doc ? <div className="documentButtonBar-button">{this.endLinkButton} </div> : null}
                 {
                     Doc.noviceMode ? null : <div className="documentButtonBar-button">{this.templateButton}</div>
                     /*<div className="documentButtonBar-button"> {this.metadataButton} </div> */
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 5783d0e57..a40599d85 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -128,48 +128,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             returnFalse,
             emptyFunction,
             undoBatch(
-                action((e, doubleTap) => {
-                    if (doubleTap && !this.props.StartLink) {
-                        if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
-                            DocumentLinksButton.StartLink = undefined;
-                            DocumentLinksButton.StartLinkView = undefined;
-                            DocumentLinksButton.AnnotationId = undefined;
-                        } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
-                            const sourceDoc = DocumentLinksButton.StartLink;
-                            const targetDoc = this.props.View.ComponentView?.getAnchor?.(true) || this.props.View.Document;
-                            const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, 'links'); //why is long drag here when this is used for completing links by clicking?
-
-                            LinkManager.currentLink = linkDoc;
-
-                            runInAction(() => {
-                                if (linkDoc) {
-                                    TaskCompletionBox.textDisplayed = 'Link Created';
-                                    TaskCompletionBox.popupX = e.screenX;
-                                    TaskCompletionBox.popupY = e.screenY - 133;
-                                    TaskCompletionBox.taskCompleted = true;
-
-                                    LinkDescriptionPopup.popupX = e.screenX;
-                                    LinkDescriptionPopup.popupY = e.screenY - 100;
-                                    LinkDescriptionPopup.descriptionPopup = true;
-
-                                    const rect = document.body.getBoundingClientRect();
-                                    if (LinkDescriptionPopup.popupX + 200 > rect.width) {
-                                        LinkDescriptionPopup.popupX -= 190;
-                                        TaskCompletionBox.popupX -= 40;
-                                    }
-                                    if (LinkDescriptionPopup.popupY + 100 > rect.height) {
-                                        LinkDescriptionPopup.popupY -= 40;
-                                        TaskCompletionBox.popupY -= 40;
-                                    }
-
-                                    setTimeout(
-                                        action(() => (TaskCompletionBox.taskCompleted = false)),
-                                        2500
-                                    );
-                                }
-                            });
-                        }
-                    }
+                action(e => {
+                    DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View);
                 })
             )
         );
@@ -280,22 +240,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                     </div>
                 ) : null}
                 {!this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? ( //if the origin node is not this node
-                    <div
-                        className={'documentLinksButton-endLink'}
-                        ref={this._linkButton}
-                        onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
-                        onClick={e =>
-                            DocumentLinksButton.StartLink &&
-                            DocumentLinksButton.finishLinkClick(
-                                e.clientX,
-                                e.clientY,
-                                DocumentLinksButton.StartLink,
-                                this.props.View.props.Document,
-                                true,
-                                this.props.View,
-                                (e.shiftKey ? { pinDocLayout: true, pinDocContent: true, pinData: { poslayoutview: true, dataannos: true, dataview: true } } : {}) as PinProps
-                            )
-                        }>
+                    <div className={'documentLinksButton-endLink'} ref={this._linkButton} onPointerDown={DocumentLinksButton.StartLink && this.completeLink}>
                         <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                     </div>
                 ) : null}
@@ -304,7 +249,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     }
 
     render() {
-        const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link (shift key captures viewing state)';
+        const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
         const buttonTitle = 'Tap to view links; double tap to open link collection';
         const title = this.props.ShowCount ? buttonTitle : menuTitle;
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2226d10b4..123499647 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -63,6 +63,7 @@ import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
 import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
+import { PinProps, PresBox } from '../trails';
 const translateGoogleApi = require('translate-google-api');
 
 export const GoogleRef = 'googleDocId';
@@ -229,7 +230,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     }
 
-    getAnchor = (addAsAnnotation: boolean) => this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+        if (!pinProps) return this.rootDoc;
+        const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true });
+        this.addDocument(anchor);
+        this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+        PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc);
+        return anchor;
+    };
 
     @action
     setupAnchorMenu = () => {
@@ -988,8 +996,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000));
             }
         }
-
-        return this._didScroll ? this._focusSpeed : didToggle ? 1 : undefined; // if we actually scrolled, then return some focusSpeed
+        const finalFocusSpeed = this._didScroll ? this._focusSpeed : didToggle ? 1 : undefined;
+        PresBox.restoreTargetDocView(docView, textAnchor, finalFocusSpeed ?? 0);
+        return finalFocusSpeed; // if we actually scrolled, then return some focusSpeed
     };
 
     // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
-- 
cgit v1.2.3-70-g09d2


From 5e7989da274606638c96f649e97e9d1a979956f5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 28 Feb 2023 20:57:01 -0500
Subject: cleaning up of following inPlace links to overlay current
 inplaceContainer contents instead of deleting them.

---
 src/client/util/LinkFollower.ts                    |  8 ++---
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 src/client/views/LightboxView.tsx                  | 11 ++----
 src/client/views/PropertiesButtons.tsx             | 39 ++++++++++------------
 src/client/views/PropertiesView.tsx                |  6 ++--
 src/client/views/collections/CollectionSubView.tsx |  1 +
 src/client/views/collections/TabDocView.tsx        | 12 ++++++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 ++-
 src/client/views/nodes/DocumentView.tsx            |  9 ++---
 src/client/views/nodes/PDFBox.tsx                  | 10 +++++-
 src/client/views/nodes/WebBox.tsx                  |  9 ++++-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  8 +++++
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 13 files changed, 72 insertions(+), 49 deletions(-)

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

diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index bbfd516da..50bc89ed2 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,11 +1,11 @@
 import { action, runInAction } from 'mobx';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
-import { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentDecorations } from '../views/DocumentDecorations';
 import { LightboxView } from '../views/LightboxView';
-import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView';
+import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView';
 import { PresBox } from '../views/nodes/trails';
 import { DocumentManager } from './DocumentManager';
 import { LinkManager } from './LinkManager';
@@ -35,7 +35,7 @@ export class LinkFollower {
         const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
             const createTabForTarget = (didFocus: boolean) =>
                 new Promise<ViewAdjustment>(res => {
-                    const where = LightboxView.LightboxDoc ? OpenWhere.inPlace : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere);
+                    const where = LightboxView.LightboxDoc ? OpenWhere.lightbox : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere);
                     docViewProps.addDocTab(doc, where);
                     setTimeout(() => {
                         const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 20c35739e..af868ba9c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -243,7 +243,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                 <div
                     className="documentButtonBar-icon documentButtonBar-follow"
                     style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
-                    onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
+                    onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false)))}>
                     <div className="documentButtonBar-followTypes">
                         {followBtn(
                             true,
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index e531bf71c..c9b1dcfd8 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -145,13 +145,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         }
     }
     public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => {
-        if (location !== OpenWhere.lightbox) {
-            const inPlaceView = DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
-            if (inPlaceView) {
-                inPlaceView.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List<Doc>([doc]);
-                return true;
-            }
-        }
         SelectionManager.DeselectAll();
         return LightboxView.SetLightboxDoc(
             doc,
@@ -318,7 +311,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} />
                 <div
                     className="lightboxView-navBtn"
-                    title={'toggle fit width'}
+                    title="toggle fit width"
                     onClick={e => {
                         e.stopPropagation();
                         LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth;
@@ -327,7 +320,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 </div>
                 <div
                     className="lightboxView-tabBtn"
-                    title={'open in tab'}
+                    title="open in tab"
                     onClick={e => {
                         e.stopPropagation();
                         CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none);
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index a152b4948..7e33ee495 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -6,6 +6,7 @@ import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
+import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, StrCast } from '../../fields/Types';
 import { ImageField } from '../../fields/URLField';
 import { DocUtils } from '../documents/Documents';
@@ -129,27 +130,21 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             onClick => {
                 SelectionManager.Views().forEach(dv => {
                     const containerDoc = dv.rootDoc;
-                    containerDoc.followAllLinks =
-                        containerDoc.noShadow =
-                        containerDoc.noHighlighting =
-                        containerDoc._isLinkButton =
-                        containerDoc._fitContentsToBox =
-                        containerDoc._forceActive =
-                        containerDoc._isInPlaceContainer =
-                            !containerDoc._isInPlaceContainer;
-                    containerDoc.followLinkLocation = containerDoc._isInPlaceContainer ? OpenWhere.inPlace : undefined;
+                    //containerDoc.followAllLinks =
+                    // containerDoc.noShadow =
+                    // containerDoc.noHighlighting =
+                    // containerDoc._forceActive =
+                    containerDoc._fitContentsToBox = containerDoc._isInPlaceContainer = !containerDoc._isInPlaceContainer;
                     containerDoc._xPadding = containerDoc._yPadding = containerDoc._isInPlaceContainer ? 10 : undefined;
-                    const menuDoc = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]).lastElement();
-                    if (menuDoc) {
-                        menuDoc.hideDecorations = menuDoc._forceActive = menuDoc._fitContentsToBox = menuDoc._isLinkButton = menuDoc._noShadow = menuDoc.noHighlighting = containerDoc._isInPlaceContainer;
-                        if (!dv.allLinks.find(link => link.anchor1 === menuDoc || link.anchor2 === menuDoc)) {
-                            DocUtils.MakeLink({ doc: dv.rootDoc }, { doc: menuDoc }, 'back link to container');
-                        }
-                        DocListCast(menuDoc[Doc.LayoutFieldKey(menuDoc)]).forEach(menuItem => {
-                            menuItem.followLinkAudio = menuItem.followAllLinks = menuItem._isLinkButton = true;
-                            menuItem._followLinkLocation = OpenWhere.inPlace;
+                    const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
+                    dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
+                    containerContents.forEach(doc => {
+                        DocListCast(doc.links).forEach(link => {
+                            doc.isLinkButton = true;
+                            //doc.followLinkLocation = OpenWhere.inPlace;
+                            link.linkDisplay = false;
                         });
-                    }
+                    });
                 });
             }
         );
@@ -309,10 +304,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                         docView.setToggleDetail();
                         break;
                     case 'linkInPlace':
-                        docView.toggleFollowLink('inPlace', false, false);
+                        docView.toggleFollowLink(false, false);
+                        docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.inPlace : undefined;
                         break;
                     case 'linkOnRight':
-                        docView.toggleFollowLink('add:right', false, false);
+                        docView.toggleFollowLink(false, false);
+                        docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.addRight : undefined;
                         break;
                 }
             });
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index f7708d801..e9d1433c3 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1406,7 +1406,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     });
 
     @undoBatch
-    changeFollowBehavior = action((follow: string) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = follow));
+    changeFollowBehavior = action((follow: Opt<string>) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = follow));
 
     @undoBatch
     changeAnimationBehavior = action((behavior: string) => this.sourceAnchor && (this.sourceAnchor.followLinkAnimEffect = behavior));
@@ -1615,8 +1615,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-section">
                                         <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
                                             <p>Follow by</p>
-                                            <select onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkLocation, 'default')}>
-                                                <option value="default">Default</option>
+                                            <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
+                                                <option value={undefined}>Default</option>
                                                 <option value={OpenWhere.addLeft}>Opening in new left pane</option>
                                                 <option value={OpenWhere.addRight}>Opening in new right pane</option>
                                                 <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 27ae3041f..c88ae314e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -67,6 +67,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         // to its children which may be templates.
         // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey'
         @computed get dataField() {
+            if (this.layoutDoc[this.props.fieldKey]) return this.layoutDoc[this.props.fieldKey];
             // sets the dataDoc's data field to an empty list if the data field is undefined - prevents issues with addonly
             // setTimeout changes it outside of the @computed section
             !this.dataDoc[this.props.fieldKey] && setTimeout(() => !this.dataDoc[this.props.fieldKey] && (this.dataDoc[this.props.fieldKey] = new List<Doc>()));
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index b75f315ca..01872df84 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -358,7 +358,17 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         if (doc.dockingConfig) return DashboardView.openDashboard(doc);
         // prettier-ignore
         switch (whereFields[0]) {
-            case OpenWhere.inPlace:  // fall through to lightbox
+            case undefined:
+            case OpenWhere.inPlace:    {
+                if (this.layoutDoc?.isInPlaceContainer) {
+                    const inPlaceView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
+                    const data = inPlaceView?.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)];
+                    if (inPlaceView && (!data || data instanceof List)) {
+                        inPlaceView.layoutDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List<Doc>([doc]);
+                        return true;
+                    }
+                }
+            }
             case OpenWhere.lightbox:   return LightboxView.AddDocTab(doc, location);
             case OpenWhere.dashboard:  return DashboardView.openDashboard(doc);
             case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0477c6a16..78804b070 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1395,6 +1395,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         );
     }
     addDocTab = action((doc: Doc, where: OpenWhere) => {
+        if (this.props.isAnnotationOverlay) return this.props.addDocTab(doc, where);
         switch (where) {
             case OpenWhere.inParent:
                 return this.props.addDocument?.(doc) || false;
@@ -1412,9 +1413,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         (!docContext || this.props.removeDocument?.(docContext))) ||
                     false
                 );
+            case undefined:
             case OpenWhere.inPlace:
                 if (this.layoutDoc.isInPlaceContainer) {
-                    this.dataDoc[this.props.fieldKey] = new List<Doc>(doc instanceof Doc ? [doc] : doc);
+                    this.layoutDoc[this.props.fieldKey] = new List<Doc>(doc instanceof Doc ? [doc] : doc);
                     return true;
                 }
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 039a75a1b..beb0f8e3a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -792,7 +792,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @undoBatch
     @action
-    toggleFollowLink = (location: Opt<string>, zoom?: boolean, setTargetToggle?: boolean): void => {
+    toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => {
         this.Document.ignoreClick = false;
         if (setTargetToggle) {
             this.Document.followLinkToggle = !this.Document.followLinkToggle;
@@ -802,7 +802,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         if (this.Document._isLinkButton && !this.onClickHandler) {
             zoom !== undefined && (this.Document.followLinkZoom = zoom);
-            this.Document.followLinkLocation = location;
         } else if (this.Document._isLinkButton && this.onClickHandler) {
             this.Document._isLinkButton = false;
             this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
@@ -985,10 +984,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
 
                     onClicks.push({ description: this.Document.ignoreClick ? 'Select' : 'Do Nothing', event: () => (this.Document.ignoreClick = !this.Document.ignoreClick), icon: this.Document.ignoreClick ? 'unlock' : 'lock' });
-                    onClicks.push({ description: this.Document.isLinkButton ? 'Remove Follow Behavior' : 'Follow Link in Place', event: () => this.toggleFollowLink('inPlace', false, false), icon: 'link' });
-                    !this.Document.isLinkButton && onClicks.push({ description: 'Follow Link on Right', event: () => this.toggleFollowLink('add:right', false, false), icon: 'link' });
-                    onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(undefined, false, false), icon: 'link' });
-                    onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
+                    onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' });
+                    onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(false, true), icon: 'map-pin' });
                     onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
                     !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
                 } else if (DocListCast(this.Document.links).length) {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 8d7ab2f0d..6b2f2246a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -22,7 +22,7 @@ import { LightboxView } from '../LightboxView';
 import { CreateImage } from '../nodes/WebBoxRenderer';
 import { PDFViewer } from '../pdf/PDFViewer';
 import { SidebarAnnos } from '../SidebarAnnos';
-import { DocFocusOptions, DocumentView } from './DocumentView';
+import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { ImageBox } from './ImageBox';
 import './PDFBox.scss';
@@ -200,6 +200,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view);
 
+    sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
+        if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+            this.toggleSidebar(false);
+            return true;
+        }
+        return this.props.addDocTab(doc, where);
+    };
     scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => {
         let didToggle = false;
         if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) {
@@ -487,6 +494,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <PDFViewer
                         {...this.props}
                         rootDoc={this.rootDoc}
+                        addDocTab={this.sidebarAddDocTab}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
                         pdf={this._pdf!}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index caab18e0d..f5eab51c2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -29,7 +29,7 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
 import { Annotation } from '../pdf/Annotation';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { StyleProp } from '../StyleProvider';
-import { DocFocusOptions, DocumentView, DocumentViewProps } from './DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { LinkDocPreview } from './LinkDocPreview';
 import { VideoBox } from './VideoBox';
@@ -322,6 +322,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return undefined;
     };
 
+    sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
+        if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+            this.toggleSidebar(false);
+            return true;
+        }
+        return this.props.addDocTab(doc, where);
+    };
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         let ele: Opt<HTMLDivElement> = undefined;
         try {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 123499647..dc7a11e6e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -230,6 +230,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     }
 
+    sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
+        if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+            this.toggleSidebar(false);
+            return true;
+        }
+        return this.props.addDocTab(doc, where);
+    };
+
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         if (!pinProps) return this.rootDoc;
         const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true });
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9877690f8..ffc49e4f3 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -16,7 +16,7 @@ import { SnappingManager } from '../../util/SnappingManager';
 import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { MarqueeAnnotator } from '../MarqueeAnnotator';
-import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentViewProps, OpenWhere } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import { StyleProp } from '../StyleProvider';
-- 
cgit v1.2.3-70-g09d2


From 8aeab8c3f25c556e39f3e9e58f9c321e79459df8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 17 Mar 2023 12:22:03 -0400
Subject: fixed scriptingbox to remove script if text is empty.  fixed
 scripting with capturedvariables not to cache scripts with lists of captured
 documents.   fixed runtime warnings with stackedTimelined and AudioBox

---
 src/client/util/Scripting.ts                             |  8 +++++++-
 src/client/views/DocumentButtonBar.tsx                   |  2 +-
 src/client/views/MainView.tsx                            |  3 +--
 src/client/views/StyleProvider.tsx                       |  2 +-
 src/client/views/TemplateMenu.tsx                        |  1 -
 .../views/collections/CollectionStackedTimeline.tsx      |  2 +-
 src/client/views/nodes/AudioBox.tsx                      | 10 +++-------
 src/client/views/nodes/DocumentView.tsx                  |  4 ----
 src/client/views/nodes/ScriptingBox.tsx                  | 16 +++++++++-------
 src/fields/Doc.ts                                        |  9 +--------
 10 files changed, 24 insertions(+), 33 deletions(-)

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

diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index d32298c83..f17a98616 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -7,7 +7,9 @@
 import * as typescriptlib from '!!raw-loader!./type_decls.d';
 import * as ts from 'typescript';
 import { Doc, Field } from '../../fields/Doc';
+import { ToScriptString } from '../../fields/FieldSymbols';
 import { ObjectField } from '../../fields/ObjectField';
+import { RefField } from '../../fields/RefField';
 import { ScriptField } from '../../fields/ScriptField';
 import { scriptingGlobals, ScriptingGlobals } from './ScriptingGlobals';
 export { ts };
@@ -180,7 +182,11 @@ function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, inde
 
 export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult {
     const captured = options.capturedVariables ?? {};
-    const signature = Object.keys(captured).reduce((p, v) => p + `${v}=${captured[v] instanceof ObjectField ? 'XXX' : captured[v].toString()}`, '');
+    const signature = Object.keys(captured).reduce((p, v) => {
+        const formatCapture = (obj: any) => `${v}=${obj instanceof RefField ? 'XXX' : obj.toString()}`;
+        if (captured[v] instanceof Array) return p + (captured[v] as any).map(formatCapture);
+        return p + formatCapture(captured[v]);
+    }, '');
     const found = ScriptField.GetScriptFieldCache(script + ':' + signature);
     if (found) return found as CompiledScript;
     const { requiredType = '', addReturn = false, params = {}, capturedVariables = {}, typecheck = true } = options;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index af868ba9c..9389fed01 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -518,7 +518,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             )
                         }>
                         <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onTemplateButton}>
-                            {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
+                            <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />
                         </div>
                     </Flyout>
                 </div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2e04ca3dd..118635a38 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -10,7 +10,7 @@ import 'normalize.css';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { ScriptField } from '../../fields/ScriptField';
-import { DocCast, StrCast } from '../../fields/Types';
+import { StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
@@ -57,7 +57,6 @@ import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
 import { RadialMenu } from './nodes/RadialMenu';
 import { TaskCompletionBox } from './nodes/TaskCompletedBox';
-import { PresBox } from './nodes/trails';
 import { OverlayView } from './OverlayView';
 import { AnchorMenu } from './pdf/AnchorMenu';
 import { PreviewCursor } from './PreviewCursor';
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index dba5d703f..b11117b11 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -5,7 +5,7 @@ import { action, runInAction } from 'mobx';
 import { extname } from 'path';
 import { Doc, Opt } from '../../fields/Doc';
 import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types';
-import { DashColor, emptyFunction, lightOrDark } from '../../Utils';
+import { DashColor, lightOrDark } from '../../Utils';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocFocusOrOpen } from '../util/DocumentManager';
 import { LinkManager } from '../util/LinkManager';
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 863829a51..681ff66e0 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,7 +1,6 @@
 import { action, computed, observable, ObservableSet, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../../fields/Doc';
-import { List } from '../../fields/List';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index f9249e7d5..302d4a464 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -565,7 +565,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             anchor,
         }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
-        return (
+        return this.clipDuration === 0 ? null : (
             <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
                 <div
                     className="collectionStackedTimeline-timelineContainer"
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 0b95d712c..610c10be6 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -634,15 +634,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                                 step="0.1"
                                 min="1"
                                 max="5"
-                                value={this.timeline?._zoomFactor}
+                                value={this.timeline?._zoomFactor ?? 1}
                                 className="toolbar-slider"
                                 id="zoom-slider"
-                                onPointerDown={(e: React.PointerEvent) => {
-                                    e.stopPropagation();
-                                }}
-                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
-                                    this.zoom(Number(e.target.value));
-                                }}
+                                onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.zoom(Number(e.target.value))}
                             />
                         </div>
                     )}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6ae102a0c..b7a760c1e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1489,10 +1489,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined,
         });
 
-        if (this.rootDoc.title === 'Italic') {
-            console.log(highlighting, this.borderRounding, highlighting?.highlightStroke);
-        }
-
         const animRenderDoc = DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[AnimationSym], this.rootDoc);
         return (
             <div
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index fa2021642..a6e2c3e90 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -180,13 +180,15 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         const params: ScriptParam = {};
         this.compileParams.forEach(p => (params[p.split(':')[0].trim()] = p.split(':')[1].trim()));
 
-        const result = CompileScript(this.rawText, {
-            editable: true,
-            transformer: DocumentIconContainer.getTransformer(),
-            params,
-            typecheck: false,
-        });
-        Doc.SetInPlace(this.rootDoc, this.fieldKey, result.compiled ? new ScriptField(result, undefined, this.rawText) : new ScriptField(undefined, undefined, this.rawText), true);
+        const result = !this.rawText.trim()
+            ? ({ compiled: false, errors: undefined } as any)
+            : CompileScript(this.rawText, {
+                  editable: true,
+                  transformer: DocumentIconContainer.getTransformer(),
+                  params,
+                  typecheck: false,
+              });
+        Doc.SetInPlace(this.rootDoc, this.fieldKey, result.compiled ? new ScriptField(result, undefined, this.rawText) : undefined, true);
         this.onError(result.compiled ? undefined : result.errors);
         return result.compiled;
     };
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 3169031b4..44314dca2 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -518,20 +518,13 @@ export namespace Doc {
     }
     export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
         if (key.startsWith('_')) key = key.substring(1);
-        const hasProto = doc.proto instanceof Doc;
+        const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined;
         const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
         const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
         if (onDeleg || !hasProto || (!onProto && !defaultProto)) {
             doc[key] = value;
         } else doc.proto![key] = value;
     }
-    export async function SetOnPrototype(doc: Doc, key: string, value: Field) {
-        const proto = Object.getOwnPropertyNames(doc).indexOf('isPrototype') === -1 ? doc.proto : doc;
-
-        if (proto) {
-            proto[key] = value;
-        }
-    }
     export function GetAllPrototypes(doc: Doc): Doc[] {
         const protos: Doc[] = [];
         let d: Opt<Doc> = doc;
-- 
cgit v1.2.3-70-g09d2


From e4b76f6c1dee63c49408c48d7b2bc98e62dc813a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 5 Apr 2023 12:19:01 -0400
Subject: adjusted general context menu items to always appear last.  cleaned
 up makeLink api,

---
 src/client/documents/Documents.ts                  | 32 +++++-----
 src/client/util/DragManager.ts                     |  1 -
 src/client/util/LinkFollower.ts                    |  2 -
 src/client/views/ContextMenu.tsx                   | 12 ++--
 src/client/views/DocumentButtonBar.tsx             |  4 +-
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/SidebarAnnos.tsx                  |  2 +-
 .../views/collections/CollectionNoteTakingView.tsx |  2 +-
 .../views/collections/CollectionStackingView.tsx   | 28 ++++-----
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 26 ++------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 70 +++++++++++++++-------
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/PDFBox.tsx                  | 31 +++++-----
 src/client/views/nodes/VideoBox.tsx                |  6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 21 files changed, 117 insertions(+), 118 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9cb480c4a..5623ca218 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -470,7 +470,7 @@ export namespace Docs {
                 DocumentType.AUDIO,
                 {
                     layout: { view: AudioBox, dataField: defaultDataKey },
-                    options: { _height: 100, forceReflow: true, nativeDimModifiable: true },
+                    options: { _height: 100, fitWidth: true, forceReflow: true, nativeDimModifiable: true },
                 },
             ],
             [
@@ -934,13 +934,13 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
 
-        export function LinkDocument(source: { doc: Doc; ctx?: Doc }, target: { doc: Doc; ctx?: Doc }, options: DocumentOptions = {}, id?: string) {
+        export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) {
             const linkDoc = InstanceFromProto(
                 Prototypes.get(DocumentType.LINK),
                 undefined,
                 {
-                    anchor1: source.doc,
-                    anchor2: target.doc,
+                    anchor1: source,
+                    anchor2: target,
                     ...options,
                 },
                 id
@@ -1294,16 +1294,14 @@ export namespace DocUtils {
         broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
         return DocUtils.ActiveRecordings.map(audio => {
             const sourceDoc = getSourceDoc();
-            const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor(true) || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
-            link && (link.followLinkLocation = OpenWhere.addRight);
-            return link;
+            return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { linkDisplay: false, linkRelationship: 'recording annotation:linked recording', description: 'recording timeline' });
         });
     }
 
-    export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = '', description: string = '', id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
-        if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
-        const sv = DocumentManager.Instance.getDocumentView(source.doc);
-        if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return;
+    export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
+        if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
+        const sv = DocumentManager.Instance.getDocumentView(source);
+        if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target) return;
         if (target.doc === Doc.UserDoc()) return undefined;
 
         const makeLink = action((linkDoc: Doc, showPopup?: number[]) => {
@@ -1343,16 +1341,16 @@ export namespace DocUtils {
                 target,
                 {
                     title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
-                    'anchor1-useLinkSmallAnchor': source.doc.useLinkSmallAnchor ? true : undefined,
-                    'anchor2-useLinkSmallAnchor': target.doc.useLinkSmallAnchor ? true : undefined,
+                    'anchor1-useLinkSmallAnchor': source.useLinkSmallAnchor ? true : undefined,
+                    'anchor2-useLinkSmallAnchor': target.useLinkSmallAnchor ? true : undefined,
                     'acl-Public': SharingPermissions.Augment,
                     '_acl-Public': SharingPermissions.Augment,
-                    linkDisplay: true,
+                    linkDisplay: linkSettings.linkDisplay,
                     _linkAutoMove: true,
-                    linkRelationship,
+                    linkRelationship: linkSettings.linkRelationship,
                     _showCaption: 'description',
                     _showTitle: 'linkRelationship',
-                    description,
+                    description: linkSettings.description,
                 },
                 id
             ),
@@ -1700,7 +1698,7 @@ export namespace DocUtils {
                 _timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
             });
             Doc.AddDocToList(context, annotationField, pushpin);
-            const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, 'pushpin', '');
+            const pushpinLink = DocUtils.MakeLink(pushpin, doc, { linkRelationship: 'pushpin' }, '');
             doc._timecodeToShow = undefined;
             return pushpin;
         }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index cc116fb46..7d2aa813f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -168,7 +168,6 @@ export namespace DragManager {
             this.dropDocCreator = dropDocCreator;
             this.offset = [0, 0];
         }
-        linkSourceDoc?: Doc;
         dropDocCreator: (annotationOn: Doc | undefined) => Doc;
         dropDocument?: Doc;
         offset: number[];
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 785018990..df61ecece 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -9,8 +9,6 @@ import { DocumentManager } from './DocumentManager';
 import { LinkManager } from './LinkManager';
 import { SelectionManager } from './SelectionManager';
 import { UndoManager } from './UndoManager';
-
-type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
 /*
  * link doc:
  * - anchor1: doc
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 6a530e3ae..e4c3e864b 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -102,13 +102,11 @@ export class ContextMenu extends React.Component {
         }
     }
     @action
-    moveAfter(item: ContextMenuProps, after: ContextMenuProps) {
-        if (after && this.findByDescription(after.description)) {
-            const curInd = this._items.findIndex(i => i.description === item.description);
-            this._items.splice(curInd, 1);
-            const afterInd = this._items.findIndex(i => i.description === after.description);
-            this._items.splice(afterInd + 1, 0, item);
-        }
+    moveAfter(item: ContextMenuProps, after?: ContextMenuProps) {
+        const curInd = this._items.findIndex(i => i.description === item.description);
+        this._items.splice(curInd, 1);
+        const afterInd = after && this.findByDescription(after.description) ? this._items.findIndex(i => i.description === after.description) : this._items.length;
+        this._items.splice(afterInd, 0, item);
     }
     @action
     setDefaultItem(prefix: string, item: (name: string) => void) {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 9389fed01..3e1a0f265 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -416,7 +416,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const targetDoc = this.view0?.props.Document;
         return !targetDoc ? null : (
             <Tooltip title={<div className="dash-tooltip">{`Open Context Menu`}</div>}>
-                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onClick={e => this.openContextMenu(e)}>
+                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onClick={this.openContextMenu}>
                     <FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
                 </div>
             </Tooltip>
@@ -569,7 +569,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc;
             const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
             if (trail !== anchor.presTrail) {
-                DocUtils.MakeLink({ doc: anchor }, { doc: trail }, 'link trail');
+                DocUtils.MakeLink(anchor, trail, { linkRelationship: 'link trail' });
                 anchor.presTrail = trail;
             }
             Doc.ActivePresentation = trail;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index befb0200d..b0888df68 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -270,6 +270,7 @@ export class MainView extends React.Component {
                 fa.faLaptopCode,
                 fa.faMale,
                 fa.faCopy,
+                fa.faHome,
                 fa.faHandPointLeft,
                 fa.faHandPointRight,
                 fa.faCompass,
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 48c0150e6..2d2b0f83e 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -74,7 +74,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
-        const link = DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on');
+        const link = DocUtils.MakeLink(anchor, target, { linkRelationship: 'inline comment:comment on' });
         link && (link.linkDisplay = false);
 
         const taggedContent = this.docFilters()
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 121260680..cb5be990d 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -441,7 +441,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
             const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
         } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 64ec419ec..1e02fc9d4 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,14 +3,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { CursorProperty } from 'csstype';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { CollectionViewType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
@@ -460,7 +460,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
             const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
         } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
@@ -623,11 +623,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!e.isPropagationStopped()) {
-            const subItems: ContextMenuProps[] = [];
-            subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
-            subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
-            subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
-            ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
+            const cm = ContextMenu.Instance;
+            const options = cm.findByDescription('Options...');
+            const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+            optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
+            optionItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+            optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' });
+            !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
         }
     };
 
@@ -735,14 +737,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                                 <EditableView {...editableViewProps} />
                             </div>
                         )}
-                        {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
-                            <Switch
-                                onChange={this.onToggle}
-                                onClick={this.onToggle}
-                                defaultChecked={true}
-                                checkedChildren="edit"
-                                unCheckedChildren="view"
-                            />} */}
                     </div>
                 </div>
             </>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5100d8d67..132ed6fb6 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -335,7 +335,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                 const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
                                 if (focusNode) {
                                     const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
-                                    anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor });
+                                    anchor && DocUtils.MakeLink(htmlDoc, anchor, {});
                                 }
                             }
                         }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 99b7549c0..75e76019e 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -359,7 +359,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (de.complete.linkDragData) {
             const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
             const destDoc = this.doc;
-            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, 'tree link', '');
+            DocUtils.MakeLink(sourceDoc, destDoc, { linkRelationship: 'tree link' });
             e.stopPropagation();
         }
         const docDragData = de.complete.docDragData;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d39668a5d..fb0bd2a19 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -397,7 +397,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
                 const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' });
                 this.props.addDocument?.(source);
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, 'annotated by:annotation of', ''); // TODODO this is where in text links get passed
+                de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { linkRelationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
             }
             e.stopPropagation(); //  do nothing if link is dropped into any freeform view parent of dragged document
             return true;
@@ -1693,6 +1693,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             },
             icon: 'compress-arrows-alt',
         });
+        if (this.props.setContentView === emptyFunction) {
+            !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
+            return;
+        }
         !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
         appearanceItems.push({
             description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`,
@@ -1700,7 +1704,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt',
         });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
-        //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
+        !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
         appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
 
         this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' });
@@ -1730,27 +1734,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
         const mores = ContextMenu.Instance.findByDescription('More...');
         const moreItems = mores && 'subitems' in mores ? mores.subitems : [];
-        moreItems.push({ description: 'Import exported collection', icon: 'upload', event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) });
         !mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' });
     };
 
-    importDocument = (x: number, y: number) => {
-        const input = document.createElement('input');
-        input.type = 'file';
-        input.accept = '.zip';
-        input.onchange = _e => {
-            input.files &&
-                Doc.importDocument(input.files[0]).then(doc => {
-                    if (doc instanceof Doc) {
-                        const [xx, yy] = this.getTransform().transformPoint(x, y);
-                        (doc.x = xx), (doc.y = yy);
-                        this.props.addDocument?.(doc);
-                    }
-                });
-        };
-        input.click();
-    };
-
     @undoBatch
     @action
     transcribeStrokes = (math: boolean) => {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 043fe0bcf..0b4f76fa1 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -527,7 +527,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         });
         const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, followLinkToggle: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
-        DocUtils.MakeLink({ doc: summary }, { doc: portal }, 'summary of:summarized by', '');
+        DocUtils.MakeLink(summary, portal, { linkRelationship: 'summary of:summarized by' });
 
         portal.hidden = true;
         this.props.addDocument?.(portal);
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index d6679b46d..df3299eef 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -142,7 +142,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             } else if (startLink !== endLink) {
                 endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
                 startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
-                const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined, undefined, undefined, true);
+                const linkDoc = DocUtils.MakeLink(startLink, endLink, { linkRelationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined }, undefined, true);
 
                 LinkManager.currentLink = linkDoc;
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c7bf37a45..159aa026a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -617,22 +617,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @undoBatch
     @action
-    drop = async (e: Event, de: DragManager.DropEvent) => {
+    drop = (e: Event, de: DragManager.DropEvent) => {
         if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return;
         if (this.props.Document === Doc.ActiveDashboard) {
             alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
             return;
         }
         const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
-        if (linkdrag) linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
-        if (linkdrag?.linkSourceDoc) {
-            e.stopPropagation();
-            if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
-                de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
-            }
-            if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
-                const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.props.Document;
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
+        if (linkdrag) {
+            linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
+            if (linkdrag.linkSourceDoc) {
+                e.stopPropagation();
+                if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
+                    de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
+                }
+                if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
+                    const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
+                    de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, undefined, [de.x, de.y - 50]);
+                }
             }
         }
     };
@@ -643,9 +645,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from');
         if (!portalLink) {
             DocUtils.MakeLink(
-                { doc: this.props.Document },
-                { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) },
-                'portal to:portal from'
+                this.props.Document,
+                Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }),
+                { linkRelationship: 'portal to:portal from' }
             );
         }
         this.Document.followLinkLocation = OpenWhere.lightbox;
@@ -653,6 +655,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         this.Document._isLinkButton = true;
     };
 
+    importDocument = () => {
+        const input = document.createElement('input');
+        input.type = 'file';
+        input.accept = '.zip';
+        input.onchange = _e => {
+            if (input.files) {
+                const batch = UndoManager.StartBatch('importing');
+                Doc.importDocument(input.files[0]).then(doc => {
+                    if (doc instanceof Doc) {
+                        this.props.addDocTab(doc, OpenWhere.addRight);
+                        batch.end();
+                    }
+                });
+            }
+        };
+        input.click();
+    };
+
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
         if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
@@ -704,9 +724,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const appearance = cm.findByDescription('UI Controls...');
             const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
-            LinkManager.Links(this.Document).length &&
-                appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? 'Show' : 'Hide'} Link Button`, event: action(() => (this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton)), icon: 'eye' });
-            !appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
+            !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
 
             if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
                 const existingOnClick = cm.findByDescription('OnClick...');
@@ -774,16 +792,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     }
                     moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' });
                 }
-                moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
-
-                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
             }
 
-            if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) {
+            !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
+        }
+        const constantItems: ContextMenuProps[] = [];
+
+        if (!Doc.IsSystem(this.rootDoc)) {
+            constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
+            constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() });
+            (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
+            if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) {
                 // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
-                moreItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
+                constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
             }
-            !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
+            cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' });
         }
         const help = cm.findByDescription('Help...');
         const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
@@ -834,7 +857,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 icon: 'book',
             });
         }
-        cm.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'question' });
+        if (!help) cm.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'question' });
+        else cm.moveAfter(help);
 
         e?.stopPropagation(); // DocumentViews should stop propagation of this event
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index b5193cd20..f38ebba27 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -222,7 +222,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         croppingProto.panYMin = anchy / viewScale;
         croppingProto.panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
+            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
             cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]();
             cropping.y = NumCast(this.rootDoc.y);
             this.props.addDocTab(cropping, OpenWhere.inParent);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index a254edd87..e38d7e2a8 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -130,7 +130,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         croppingProto['data-nativeWidth'] = anchw;
         croppingProto['data-nativeHeight'] = anchh;
         if (addCrop) {
-            DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
+            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
         }
         this.props.bringToFront(cropping);
 
@@ -344,7 +344,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             },
             (e, movement, isClick) => !isClick && batch.end(),
             () => {
-                this.toggleSidebar();
+                onButton && this.toggleSidebar();
                 batch.end();
             }
         );
@@ -440,17 +440,20 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
         return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
     };
+    @undoBatch
+    toggleSidebarType = () => (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform);
     specificContextMenu = (e: React.MouseEvent): void => {
-        const funcs: ContextMenuProps[] = [];
-        funcs.push({
-            description: 'Toggle Sidebar Type',
-            event: () => (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform),
-            icon: 'expand-arrows-alt',
-        });
-        funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
-        funcs.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
-        //funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
-        ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
+        const cm = ContextMenu.Instance;
+        const options = cm.findByDescription('Options...');
+        const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+        optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' });
+        !Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
+        //optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
+        !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' });
+        const help = cm.findByDescription('Help...');
+        const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
+        helpItems.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
+        !help && ContextMenu.Instance.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'asterisk' });
     };
 
     @computed get renderTitleBox() {
@@ -523,8 +526,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
-                    //PanelWidth={this.sidebarWidth}
-                    //ScreenToLocalTransform={this.sidebarScreenToLocal}
                 />
             ) : (
                 <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}>
@@ -568,7 +569,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
         return !this._pdf ? null : (
             <div
-                className={'pdfBox'}
+                className="pdfBox"
                 onContextMenu={this.specificContextMenu}
                 style={{
                     display: this.props.thumbShown?.() ? 'none' : undefined,
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 0570c7fcb..e14ad4b05 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -327,7 +327,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 _isLinkButton: true,
             });
             this.props.addDocument?.(b);
-            DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, 'video snapshot');
+            DocUtils.MakeLink(b, this.rootDoc, { linkRelationship: 'video snapshot' });
             Networking.PostToServer('/youtubeScreenshot', {
                 id: this.youtubeVideoId,
                 timecode: this.layoutDoc._currentTimecode,
@@ -376,7 +376,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         Doc.SetNativeWidth(Doc.GetProto(imageSnapshot), Doc.NativeWidth(this.layoutDoc));
         Doc.SetNativeHeight(Doc.GetProto(imageSnapshot), Doc.NativeHeight(this.layoutDoc));
         this.props.addDocument?.(imageSnapshot);
-        const link = DocUtils.MakeLink({ doc: imageSnapshot }, { doc: this.getAnchor(true) }, 'video snapshot');
+        const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { linkRelationship: 'video snapshot' });
         link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
         setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true));
     };
@@ -1031,7 +1031,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         croppingProto.panYMin = anchy / viewScale;
         croppingProto.panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
+            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
         }
         this.props.bringToFront(cropping);
         return cropping;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d8afc3227..361e000f9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -454,7 +454,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             alink =
                                 alink ??
                                 (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) ||
-                                    DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!);
+                                    DocUtils.MakeLink(this.props.Document, target, { linkRelationship: LinkManager.AutoKeywords })!);
                             newAutoLinks.add(alink);
                             const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
                             allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
@@ -1273,7 +1273,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         ]),
                     ]);
 
-                    const link = DocUtils.MakeLink({ doc: pdfAnchor }, { doc: this.rootDoc }, 'PDF pasted');
+                    const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { linkRelationship: 'PDF pasted' });
                     if (link) {
                         view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
                     }
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 9e9b61db3..e691869cc 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -259,7 +259,7 @@ export class RichTextRules {
                                 this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
                             }
                             const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500 }, docId);
-                            DocUtils.MakeLink({ doc: this.TextBox.getAnchor(true) }, { doc: target }, 'portal to:portal from', undefined);
+                            DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { linkRelationship: 'portal to:portal from' });
 
                             const fstate = this.TextBox.EditorView?.state;
                             if (fstate && selection) {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index fe9b13fbe..8f93f1150 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -117,7 +117,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     makeLink = action((linkTo: Doc) => {
         const linkFrom = this.props.linkCreateAnchor?.();
         if (linkFrom) {
-            const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
+            const link = DocUtils.MakeLink(linkFrom, linkTo, {});
             link && this.props.linkCreated?.(link);
         }
     });
-- 
cgit v1.2.3-70-g09d2


From 38eb3a1b3f005b79a222f76b730e883b7e2b5253 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 5 Apr 2023 14:36:37 -0400
Subject: made pinning from topbar available for all docs.

---
 src/client/util/CurrentUserUtils.ts                |  8 +++----
 src/client/views/DocumentButtonBar.tsx             | 25 +++++++++++-----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 16 +++++++++-----
 src/client/views/nodes/trails/PresBox.tsx          |  1 -
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 5 files changed, 28 insertions(+), 24 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 753cd1cb7..9571209b0 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -682,9 +682,9 @@ export class CurrentUserUtils {
                     CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
                     CollectionViewType.Grid, CollectionViewType.NoteTaking]),
               title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                                                                    ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
-            { title: "Pin",     icon: "map-pin",       toolTip: "Pin View to Trail",    btnType: ButtonType.ClickButton,         expertMode: false, toolType:"tab",                       funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, width: 20,  scripts: { onClick: 'pinWithView(_readOnly_, altKey)'}},
-            { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         expertMode: false, toolType:"tab",                                                                                          ignoreClick: true, width: 20,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected
-            { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         expertMode: false, toolType:"tab",                                                                                          ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}},
+            { title: "Pin",     icon: "map-pin",       toolTip: "Pin View to Trail",    btnType: ButtonType.ClickButton,         expertMode: false,                                                                                                                             width: 20,  scripts: { onClick: 'pinWithView(altKey)'}},
+            { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true, width: 20,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected
+            { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}},
             { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'},       scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Z order", icon: "z",             toolTip: "Bring Forward on Drag",btnType: ButtonType.ToggleButton,        expertMode: false, toolType:CollectionViewType.Freeform, funcs: {},                                                                                        scripts: { onClick: 'toggleRaiseOnDrag(_readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 20,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
@@ -693,7 +693,7 @@ export class CurrentUserUtils {
             { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Ink",     icon: "Ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    expertMode: false, toolType:DocumentType.INK,            funcs: {                                                                             linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
             { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
+            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
             { title: "Schema",  icon: "Schema",        toolTip: "Schema functions",     subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected
         ];
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 3e1a0f265..e83ea00cf 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -289,7 +289,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     @observable subEndLink = '';
     @computed
     get endLinkButton() {
-        const linkBtn = (pinDocLayout: boolean, pinDocContent: boolean, icon: IconProp) => {
+        const linkBtn = (pinLayout: boolean, pinContent: boolean, icon: IconProp) => {
             const tooltip = `Finish Link and Save ${this.subEndLink} data`;
             return !this.view0 ? null : (
                 <Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
@@ -300,7 +300,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             key={icon.toString()}
                             size="sm"
                             icon={icon}
-                            onPointerEnter={action(e => (this.subEndLink = (pinDocLayout ? 'Layout' : '') + (pinDocLayout && pinDocContent ? ' &' : '') + (pinDocContent ? ' Content' : '')))}
+                            onPointerEnter={action(e => (this.subEndLink = (pinLayout ? 'Layout' : '') + (pinLayout && pinContent ? ' &' : '') + (pinContent ? ' Content' : '')))}
                             onPointerLeave={action(e => (this.subEndLink = ''))}
                             onClick={e => {
                                 const docs = this.props
@@ -309,9 +309,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                                     .map(dv => dv!.rootDoc);
                                 this.view0 &&
                                     DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.view0.props.Document, true, this.view0, {
-                                        pinDocLayout,
-                                        pinDocContent,
-                                        pinData: !pinDocContent ? {} : { poslayoutview: true, dataannos: true, dataview: true },
+                                        pinDocLayout: pinLayout,
+                                        pinData: !pinContent ? {} : { poslayoutview: true, dataannos: true, dataview: pinContent },
                                     } as PinProps);
 
                                 e.stopPropagation();
@@ -337,7 +336,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     @computed
     get pinButton() {
         const targetDoc = this.view0?.props.Document;
-        const pinBtn = (pinDocLayout: boolean, pinDocContent: boolean, icon: IconProp) => {
+        const pinBtn = (pinLayoutView: boolean, pinContentView: boolean, icon: IconProp) => {
             const tooltip = `Pin Document and Save ${this.subPin} to trail`;
             return !tooltip ? null : (
                 <Tooltip title={<div className="dash-tooltip">{tooltip}</div>}>
@@ -351,10 +350,10 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             onPointerEnter={action(
                                 e =>
                                     (this.subPin =
-                                        (pinDocLayout ? 'Layout' : '') +
-                                        (pinDocLayout && pinDocContent ? ' &' : '') +
-                                        (pinDocContent ? ' Content View' : '') +
-                                        (pinDocLayout && pinDocContent ? '(shift+alt)' : pinDocLayout ? '(shift)' : pinDocContent ? '(alt)' : ''))
+                                        (pinLayoutView ? 'Layout' : '') +
+                                        (pinLayoutView && pinContentView ? ' &' : '') +
+                                        (pinContentView ? ' Content View' : '') +
+                                        (pinLayoutView && pinContentView ? '(shift+alt)' : pinLayoutView ? '(shift)' : pinContentView ? '(alt)' : ''))
                             )}
                             onPointerLeave={action(e => (this.subPin = ''))}
                             onClick={e => {
@@ -364,8 +363,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                                     .map(dv => dv!.rootDoc);
                                 TabDocView.PinDoc(docs, {
                                     pinAudioPlay: true,
-                                    pinDocLayout,
-                                    pinData: { dataview: true },
+                                    pinDocLayout: pinLayoutView,
+                                    pinData: { dataview: pinContentView },
                                     activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null),
                                     currentFrame: Cast(docs.lastElement()?.currentFrame, 'number', null),
                                 });
@@ -385,7 +384,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             .views()
                             .filter(v => v)
                             .map(dv => dv!.rootDoc);
-                        TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinDocContent: e.altKey, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+                        TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: {dataview: e.altKey}, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
                         e.stopPropagation();
                     }}>
                     <div className="documentButtonBar-pinTypes">
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index fb0bd2a19..22a2a429f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -2196,11 +2196,17 @@ ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
     if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent';
     runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.()));
 });
-ScriptingGlobals.add(function pinWithView(readOnly: boolean, pinDocContent: boolean) {
-    !readOnly &&
-        SelectionManager.Views().forEach(view =>
-            TabDocView.PinDoc(view.rootDoc, { currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) })
-        );
+ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
+    SelectionManager.Views().forEach(view =>
+        view.props.pinToPres(view.rootDoc, {
+            currentFrame: Cast(view.rootDoc.currentFrame, 'number', null),
+            pinData: {
+                poslayoutview: pinContent,
+                dataview: pinContent,
+            },
+            pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()),
+        })
+    );
 });
 ScriptingGlobals.add(function bringToFront() {
     SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc));
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0afb36214..bfaae8069 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -59,7 +59,6 @@ export interface PinProps {
     hidePresBox?: boolean;
     pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected)
     pinDocLayout?: boolean; // pin layout info (width/height/x/y)
-    pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text)
     pinAudioPlay?: boolean; // pin audio annotation
     pinData?: pinDataTypes;
 }
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 698a2817e..9a74b5dba 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -312,7 +312,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     updateCapturedViewContents = (presTargetDoc: Doc, activeItem: Doc) => {
         const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc;
-        PresBox.pinDocView(activeItem, { pinDocContent: true, pinData: PresBox.pinDataTypes(target) }, target);
+        PresBox.pinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target);
     };
 
     @computed get recordingIsInOverlay() {
-- 
cgit v1.2.3-70-g09d2


From 8a1267faf796b2e2a30a6ba9f86879854e9ee983 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 11 Apr 2023 13:51:44 -0400
Subject: removed arrangeItems context button for collections to make it a
 funciton on drop instead of a reaction.  Converted isLinkButton to be an
 onClick script.  got rid of unused PARAMS field on templates.   fixed
 PresElementBox rendering of embedded docs.

---
 src/client/documents/Documents.ts                  |  7 +--
 src/client/util/CurrentUserUtils.ts                |  4 +-
 src/client/util/DropConverter.ts                   | 10 +---
 src/client/util/LinkFollower.ts                    |  6 ++
 src/client/views/DocumentButtonBar.tsx             |  9 +--
 src/client/views/MarqueeAnnotator.tsx              |  8 ++-
 src/client/views/PropertiesButtons.tsx             |  7 ++-
 src/client/views/StyleProvider.tsx                 |  5 +-
 src/client/views/collections/CollectionMenu.tsx    | 19 +-----
 .../views/collections/CollectionNoteTakingView.tsx |  4 +-
 .../collections/CollectionStackedTimeline.tsx      |  6 +-
 .../views/collections/CollectionStackingView.tsx   |  6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 56 ++++++++++--------
 src/client/views/nodes/DocumentContentsView.tsx    |  3 +-
 src/client/views/nodes/DocumentView.tsx            | 67 +++++-----------------
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/nodes/VideoBox.tsx                |  7 ++-
 .../views/nodes/formattedText/DashFieldView.tsx    |  4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 src/fields/Doc.ts                                  | 10 ++--
 src/fields/documentSchemas.ts                      |  1 -
 24 files changed, 104 insertions(+), 147 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 979b294e0..7600fcac9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -263,7 +263,6 @@ export class DocumentOptions {
     caption?: RichTextField;
     opacity?: number;
     defaultBackgroundColor?: string;
-    _isLinkButton?: boolean; // marks a document as a button that will follow its primary link when clicked
     _linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint
     hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed
     isFolder?: boolean;
@@ -508,13 +507,13 @@ export namespace Docs {
                     layout: { view: LinkBox, dataField: defaultDataKey },
                     options: {
                         childDontRegisterViews: true,
-                        _isLinkButton: true,
+                        onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
                         hideLinkAnchors: true,
                         _height: 150,
                         description: '',
                         showCaption: 'description',
                         backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
-                        _removeDropProperties: new List(['isLinkButton']),
+                        _removeDropProperties: new List(['onClick']),
                     },
                 },
             ],
@@ -1695,7 +1694,7 @@ export namespace DocUtils {
                 _width: 15,
                 _height: 15,
                 _xPadding: 0,
-                _isLinkButton: true,
+                onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
                 _timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
             });
             Doc.AddDocToList(context, annotationField, pushpin);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 623aaf357..3d0db04b4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -271,7 +271,7 @@ export class CurrentUserUtils {
             {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _fitWidth: true, _showSidebar: true,  }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
             {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
-            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, _isLinkButton: true }},
+            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: ScriptField.MakeScript("followLink(this,altKey)", {altKey:"boolean"}) }},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
             {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, treeViewHideUnrendered: true}},
@@ -622,7 +622,7 @@ export class CurrentUserUtils {
             { title: "Snap\xA0Lines",icon: "th",          toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "View\xA0All",  icon: "object-group",toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Clusters",     icon: "braille",     toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
-            { title: "Arrange",      icon: "window",      toolTip: "Toggle Auto Arrange", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange",    funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Arrange",      icon: "window",      toolTip: "Toggle Auto Arrange", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange",    funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Reset",        icon: "check",       toolTip: "Reset View",          btnType: ButtonType.ClickButton,  expertMode: false, backgroundColor:"transparent",    scripts: { onClick: 'resetView()'}}, // Only when floating document is selected in freeform
         ]
     }
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 7c209d1e0..2e1d6ba23 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -33,15 +33,7 @@ function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = und
     let any = false;
     docs.forEach(d => {
         if (!StrCast(d.title).startsWith('-')) {
-            const params = StrCast(d.title)
-                .match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1]
-                .replace('()', '');
-            if (params) {
-                any = makeTemplate(d, false) || any;
-                d.PARAMS = params;
-            } else {
-                any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any;
-            }
+            any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any;
         } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) {
             any = makeTemplate(d, false) || any;
         }
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index df61ecece..f5aae6632 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -7,6 +7,7 @@ import { DocFocusOptions, DocumentViewSharedProps, OpenWhere } from '../views/no
 import { PresBox } from '../views/nodes/trails';
 import { DocumentManager } from './DocumentManager';
 import { LinkManager } from './LinkManager';
+import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 import { UndoManager } from './UndoManager';
 /*
@@ -118,3 +119,8 @@ export class LinkFollower {
         });
     }
 }
+
+ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) {
+    SelectionManager.DeselectAll();
+    LinkFollower.FollowLink(undefined, doc, altKey);
+});
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index e83ea00cf..7e1b32398 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -5,7 +5,7 @@ import { action, computed, observable, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { Cast, DocCast, NumCast } from '../../fields/Types';
+import { Cast, DocCast, NumCast, ScriptCast } from '../../fields/Types';
 import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
@@ -238,11 +238,12 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                 </Tooltip>
             );
         };
+        const followLink = ScriptCast(targetDoc?.onClick)?.script.originalScript.includes('followLink(');
         return !targetDoc ? null : (
             <Tooltip title={<div className="dash-tooltip">Set onClick to follow primary link</div>}>
                 <div
                     className="documentButtonBar-icon documentButtonBar-follow"
-                    style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
+                    style={{ backgroundColor: followLink ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: followLink ? Colors.BLACK : Colors.WHITE }}
                     onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false)))}>
                     <div className="documentButtonBar-followTypes">
                         {followBtn(
@@ -384,7 +385,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             .views()
                             .filter(v => v)
                             .map(dv => dv!.rootDoc);
-                        TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: {dataview: e.altKey}, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+                        TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
                         e.stopPropagation();
                     }}>
                     <div className="documentButtonBar-pinTypes">
@@ -593,7 +594,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         <LinkPopup
                             key="popup"
                             showPopup={this._showLinkPopup}
-                            linkCreated={link => (link.linkDisplay = !this.props.views().lastElement()?.rootDoc.isLinkButton)}
+                            linkCreated={link => (link.linkDisplay = !ScriptCast(this.props.views().lastElement()?.rootDoc.onClick)?.script.originalScript.includes('followLink('))}
                             linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
                             linkFrom={() => this.props.views().lastElement()?.rootDoc}
                         />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 30867a774..6b4f8ad85 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -14,6 +14,7 @@ import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { AnchorMenu } from './pdf/AnchorMenu';
 import React = require('react');
+import { ScriptField } from '../../fields/ScriptField';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
@@ -146,7 +147,12 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             const scale = this.props.scaling?.() || 1;
             const anno = savedAnnos[0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
-            const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: 'Annotation on ' + this.props.rootDoc.title });
+            const marqueeAnno = Docs.Create.FreeformDocument([], {
+                onClick: isLinkButton ? ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }) : undefined,
+                backgroundColor: color,
+                annotationOn: this.props.rootDoc,
+                title: 'Annotation on ' + this.props.rootDoc.title,
+            });
             marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
             marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1) + NumCast(this.props.scrollTop);
             marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 11e9dd9c9..0723966e1 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -289,6 +289,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             .filter(dv => dv.docView)
             .map(dv => dv.docView!)
             .forEach(docView => {
+                const linkButton = ScriptCast(docView.props.Document.onClick)?.script.originalScript.includes('followLink(');
                 docView.noOnClick();
                 switch (onClick) {
                     case 'enterPortal':
@@ -299,11 +300,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                         break;
                     case 'linkInPlace':
                         docView.toggleFollowLink(false, false);
-                        docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.lightbox : undefined;
+                        docView.props.Document.followLinkLocation = linkButton ? OpenWhere.lightbox : undefined;
                         break;
                     case 'linkOnRight':
                         docView.toggleFollowLink(false, false);
-                        docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.addRight : undefined;
+                        docView.props.Document.followLinkLocation = linkButton ? OpenWhere.addRight : undefined;
                         break;
                 }
             });
@@ -326,7 +327,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         ];
         const list = buttonList.map(value => {
             const click = () => this.handleOptionChange(value[0]);
-            const linkButton = BoolCast(this.selectedDoc._isLinkButton);
+            const linkButton = BoolCast(ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('followLink('));
             const followLoc = this.selectedDoc._followLinkLocation;
             const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox);
 
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index f7b9420a7..8d67f0528 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -4,7 +4,7 @@ import { Shadows } from 'browndash-components';
 import { action, runInAction } from 'mobx';
 import { extname } from 'path';
 import { Doc, Opt, StrListCast } from '../../fields/Doc';
-import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
 import { DashColor, lightOrDark, Utils } from '../../Utils';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager';
@@ -279,7 +279,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.BoxShadow: {
             if (!doc || opacity() === 0 || doc.noShadow) return undefined; // if it's not visible, then no shadow)
             if (doc.boxShadow === 'standard') return Shadows.STANDARD_SHADOW;
-            if (doc?.isLinkButton && LinkManager.Links(doc).length && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em');
+            if (ScriptCast(doc?.onClick)?.script.originalScript.includes('followLink(') && LinkManager.Links(doc).length && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any))
+                return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em');
             switch (doc?.type) {
                 case DocumentType.COL:
                     return StrCast(
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index c83f4e689..225743748 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -246,23 +246,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         immediate: undoBatch((source: Doc[]) => {}),
         initialize: emptyFunction,
     };
-    _openLinkInCommand = {
-        params: ['target', 'container'],
-        title: 'link follow target',
-        script: `{ if (self.container?.length) {
-            getProto(self.target).linkContainer = self.container[0];
-            getProto(self.target).isLinkButton = true;
-            getProto(self.target).onClick = makeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])");
-            }}`,
-        immediate: undoBatch((container: Doc[]) => {
-            if (container.length) {
-                Doc.GetProto(this.target).linkContainer = container[0];
-                Doc.GetProto(this.target).isLinkButton = true;
-                Doc.GetProto(this.target).onClick = ScriptField.MakeScript('getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])');
-            }
-        }),
-        initialize: emptyFunction,
-    };
     _viewCommand = {
         params: ['target'],
         title: 'bookmark view',
@@ -328,7 +311,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand];
     }
     @computed get _doc_commands() {
-        return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand];
+        return Doc.noviceMode ? undefined : [this._onClickCommand];
     }
     @computed get _tree_commands() {
         return undefined;
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index cb5be990d..aec0734b4 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -215,7 +215,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     blockPointerEventsWhenDragging = () => (this.docsDraggedRowCol.length ? 'none' : undefined);
     // getDisplayDoc returns the rules for displaying a document in this view (ie. DocumentView)
     getDisplayDoc(doc: Doc, width: () => number) {
-        const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
+        const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc;
         const height = () => this.getDocHeight(doc);
         let dref: Opt<DocumentView>;
         const noteTakingDocTransform = () => this.getDocTransform(doc, dref);
@@ -293,7 +293,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     getDocHeight(d?: Doc) {
         if (!d || d.hidden) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
-        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS ? undefined : this.props.DataDoc;
+        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 642d29d2a..5891cfe78 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -7,7 +7,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { Cast, NumCast } from '../../../fields/Types';
+import { Cast, NumCast, ScriptCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
@@ -406,7 +406,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
                 useLinkSmallAnchor: true,
                 hideLinkButton: true,
-                _isLinkButton: true,
+                onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
                 annotationOn: rootDoc,
                 _timelineLabel: true,
                 borderRounding: anchorEndTime === undefined ? '100%' : undefined,
@@ -450,7 +450,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     @action
     clickAnchor = (anchorDoc: Doc, clientX: number) => {
-        if (anchorDoc.isLinkButton) {
+        if (ScriptCast(anchorDoc.onClick)?.script.originalScript.includes('followLink(')) {
             LinkFollower.FollowLink(undefined, anchorDoc, false);
         }
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 1e02fc9d4..a85ee0e02 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -305,7 +305,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     // this is what renders the document that you see on the screen
     // called in Children: this actually adds a document to our children list
     getDisplayDoc(doc: Doc, width: () => number, count: number) {
-        const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
+        const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc;
         const height = () => this.getDocHeight(doc);
 
         let dref: Opt<DocumentView>;
@@ -323,7 +323,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 styleProvider={this.styleProvider}
                 docViewPath={this.props.docViewPath}
                 fitWidth={this.props.childFitWidth}
-                isContentActive={doc.isLinkButton ? this.isChildButtonContentActive : this.isChildContentActive}
+                isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
                 onBrowseClick={this.props.onBrowseClick}
                 isDocumentActive={this.isContentActive}
@@ -381,7 +381,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     getDocHeight(d?: Doc) {
         if (!d || d.hidden) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
-        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS ? undefined : this.props.DataDoc;
+        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1ecc69174..7ae7be3c8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -349,6 +349,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1));
         const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
         const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
+
         for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
             const d = docDragData.droppedDocuments[i];
             const layoutDoc = Doc.Layout(d);
@@ -370,6 +371,30 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
+        if (this.layoutDoc._autoArrange || de.metaKey) {
+            const sorted = this.childLayoutPairs.slice().sort((a, b) => NumCast(a.layout.y) - NumCast(b.layout.y));
+            sorted.splice(
+                sorted.findIndex(pair => pair.layout === refDoc),
+                1
+            );
+            if (sorted.length && refDoc && NumCast(sorted[0].layout.y) < NumCast(refDoc.y)) {
+                const topIndexed = NumCast(refDoc.y) < NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height);
+                const deltay = sorted.length > 1 ? NumCast(refDoc.y) - (NumCast(sorted[0].layout.y) + (topIndexed ? 0 : NumCast(sorted[0].layout._height))) : 0;
+                const deltax = sorted.length > 1 ? NumCast(refDoc.x) - NumCast(sorted[0].layout.x) : 0;
+
+                let lastx = NumCast(refDoc.x);
+                let lasty = NumCast(refDoc.y) + (topIndexed ? 0 : NumCast(refDoc._height));
+                setTimeout(
+                    action(() =>
+                        sorted.slice(1).forEach((pair, i) => {
+                            lastx = pair.layout.x = lastx + deltax;
+                            lasty = (pair.layout.y = lasty + deltay) + (topIndexed ? 0 : NumCast(pair.layout._height));
+                        })
+                    )
+                );
+            }
+        }
+
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
         return true;
     }
@@ -1409,24 +1434,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     doFreeformLayout(poolData: Map<string, PoolData>) {
-        if (this.layoutDoc._autoArrange) {
-            const sorted = this.childLayoutPairs.slice().sort((a, b) => (NumCast(a.layout.y) < NumCast(b.layout.y) ? -1 : 1));
-            if (sorted.length > 1) {
-                const deltay = sorted.length > 1 ? NumCast(sorted[1].layout.y) - (NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height)) : 0;
-                const deltax = sorted.length > 1 ? NumCast(sorted[1].layout.x) - NumCast(sorted[0].layout.x) : 0;
-
-                let lastx = NumCast(sorted[0].layout.x);
-                let lasty = NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height);
-                setTimeout(
-                    action(() =>
-                        sorted.slice(1).forEach((pair, i) => {
-                            lastx = pair.layout.x = lastx + deltax;
-                            lasty = (pair.layout.y = lasty + deltay) + NumCast(pair.layout._height);
-                        })
-                    )
-                );
-            }
-        }
         this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document })));
         return [] as ViewDefResult[];
     }
@@ -1712,11 +1719,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             },
             icon: 'compress-arrows-alt',
         });
-        appearanceItems.push({
-            description: 'Toggle auto arrange',
-            event: () => (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange),
-            icon: 'compress-arrows-alt',
-        });
+        !Doc.noviceMode &&
+            appearanceItems.push({
+                description: 'Toggle auto arrange',
+                event: () => (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange),
+                icon: 'compress-arrows-alt',
+            });
         if (this.props.setContentView === emptyFunction) {
             !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
             return;
@@ -1729,7 +1737,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
         !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
-        appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
+        this.props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
 
         this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' });
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 9e56de8c2..2d12005fb 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -139,7 +139,6 @@ export class DocumentContentsView extends React.Component<
         return proto instanceof Promise ? undefined : proto;
     }
     get layoutDoc() {
-        const params = StrCast(this.props.Document.PARAMS);
         // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
         // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
         const template: Doc =
@@ -147,7 +146,7 @@ export class DocumentContentsView extends React.Component<
             (this.props.LayoutTemplateString && this.props.Document) ||
             (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
             Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        return Doc.expandTemplateLayout(template, this.props.Document, params ? '(' + params + ')' : this.props.layoutKey);
+        return Doc.expandTemplateLayout(template, this.props.Document, this.props.layoutKey);
     }
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e24bf35f5..a39c3cf8f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -469,11 +469,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             ? this.props.select(false)
                             : '';
                     clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
-                } else if (!this.disableClickScriptFunc && this.allLinks.length && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
-                    clickFunc = () => {
-                        SelectionManager.DeselectAll();
-                        LinkFollower.FollowLink(undefined, this.Document, e.altKey);
-                    };
                 } else {
                     // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
@@ -563,48 +558,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch
     @action
     toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => {
-        this.Document.ignoreClick = false;
-        if (setTargetToggle) {
-            this.Document.followLinkToggle = !this.Document.followLinkToggle;
-            this.Document._isLinkButton = this.Document.followLinkToggle || this.Document._isLinkButton;
-        } else {
-            this.Document._isLinkButton = !this.Document._isLinkButton;
-        }
-        if (this.Document._isLinkButton && !this.onClickHandler) {
-            zoom !== undefined && (this.Document.followLinkZoom = zoom);
-        } else if (this.Document._isLinkButton && this.onClickHandler) {
-            this.Document._isLinkButton = false;
-            this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
-        }
-    };
-    @undoBatch
-    @action
-    toggleTargetOnClick = (): void => {
-        this.Document.ignoreClick = false;
-        this.Document._isLinkButton = true;
-        this.Document.followLinkToggle = true;
+        const hadOnClick = this.rootDoc.onClick;
+        this.noOnClick();
+        this.Document.onClick = hadOnClick ? undefined : ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
+        this.Document.waitForDoubleClickToClick = hadOnClick ? undefined : 'never';
     };
     @undoBatch
     @action
-    followLinkOnClick = (location: Opt<string>, zoom: boolean): void => {
+    followLinkOnClick = (): void => {
         this.Document.ignoreClick = false;
-        this.Document._isLinkButton = true;
+        this.Document.onClick = ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
         this.Document.followLinkToggle = false;
-        this.Document.followLinkZoom = zoom;
-        this.Document.followLinkLocation = location;
-    };
-    @undoBatch
-    @action
-    selectOnClick = (): void => {
-        this.Document.ignoreClick = false;
-        this.Document._isLinkButton = false;
-        this.Document.followLinkToggle = false;
-        this.Document.onClick = this.layoutDoc.onClick = undefined;
+        this.Document.followLinkZoom = false;
+        this.Document.followLinkLocation = undefined;
     };
     @undoBatch
     noOnClick = (): void => {
         this.Document.ignoreClick = false;
-        this.Document._isLinkButton = false;
+        this.Document.onClick = Doc.GetProto(this.Document).onClick = undefined;
     };
 
     @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
@@ -652,8 +623,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             );
         }
         this.Document.followLinkLocation = OpenWhere.lightbox;
-        this.Document.followLinkZoom = true;
-        this.Document._isLinkButton = true;
+        this.Document.onClick = ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
     };
 
     importDocument = () => {
@@ -744,29 +714,20 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     !zorders && cm.addItem({ description: 'ZOrder...', noexpand: true, subitems: zorderItems, icon: 'compass' });
                 }
 
-                !Doc.noviceMode && onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' });
+                onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' });
                 !Doc.noviceMode && onClicks.push({ description: 'Toggle Detail', event: this.setToggleDetail, icon: 'concierge-bell' });
-                this.props.CollectionFreeFormDocumentView &&
-                    onClicks.push({
-                        description: (this.Document.followLinkZoom ? "Don't" : '') + ' zoom following link',
-                        event: () => (this.Document.followLinkZoom = !this.Document.followLinkZoom),
-                        icon: this.Document.ignoreClick ? 'unlock' : 'lock',
-                    });
 
                 if (!this.Document.annotationOn) {
                     const options = cm.findByDescription('Options...');
                     const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
                     !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
 
-                    onClicks.push({ description: this.Document.ignoreClick ? 'Select' : 'Do Nothing', event: () => (this.Document.ignoreClick = !this.Document.ignoreClick), icon: this.Document.ignoreClick ? 'unlock' : 'lock' });
-                    onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' });
-                    onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(false, true), icon: 'map-pin' });
+                    onClicks.push({ description: this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' });
                     onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
                     !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
                 } else if (LinkManager.Links(this.Document).length) {
-                    onClicks.push({ description: 'Select on Click', event: () => this.selectOnClick(), icon: 'link' });
-                    onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(undefined, false), icon: 'link' });
-                    onClicks.push({ description: 'Toggle Link Target on Click', event: () => this.toggleTargetOnClick(), icon: 'map-pin' });
+                    onClicks.push({ description: 'Select on Click', event: () => this.noOnClick(), icon: 'link' });
+                    onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(), icon: 'link' });
                     !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, subitems: onClicks, icon: 'mouse-pointer' });
                 }
             }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f38ebba27..22ecaa299 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -202,7 +202,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.y = NumCast(this.rootDoc.y);
         cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
         cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
-        cropping.isLinkButton = undefined;
+        cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
         croppingProto.isPrototype = true;
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 3feb95ce9..31f1775e5 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -47,7 +47,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.rootDoc]);
                 dragData.dropAction = 'alias';
-                dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'isLinkButton'];
+                dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'onClick'];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                 return true;
             } else {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index e38d7e2a8..502811e35 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -119,7 +119,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         cropping.y = NumCast(this.rootDoc.y);
         cropping._width = anchw;
         cropping._height = anchh;
-        cropping.isLinkButton = undefined;
+        cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
         croppingProto.isPrototype = true;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e14ad4b05..83ad5628c 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -33,6 +33,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { RecordingBox } from './RecordingBox';
 import { PinProps, PresBox } from './trails';
 import './VideoBox.scss';
+import { ScriptField } from '../../../fields/ScriptField';
 const path = require('path');
 
 /**
@@ -324,7 +325,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 _width: 150,
                 _height: 50,
                 title: (this.layoutDoc._currentTimecode || 0).toString(),
-                _isLinkButton: true,
+                onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
             });
             this.props.addDocument?.(b);
             DocUtils.MakeLink(b, this.rootDoc, { linkRelationship: 'video snapshot' });
@@ -368,7 +369,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             _nativeHeight: Doc.NativeHeight(this.layoutDoc),
             x: NumCast(this.layoutDoc.x) + width,
             y: NumCast(this.layoutDoc.y),
-            _isLinkButton: true,
+            onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
             _width: 150,
             _height: (height / width) * 150,
             title: '--snapshot' + NumCast(this.layoutDoc._currentTimecode) + ' image-',
@@ -1010,7 +1011,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
         cropping.timecodeToHide = undefined;
         cropping.timecodeToShow = undefined;
-        cropping.isLinkButton = undefined;
+        cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
         croppingProto.isPrototype = true;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 21ccf3bc7..f23426bb3 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -116,7 +116,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
 
     public static multiValueDelimeter = ';';
     public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
-        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === 'PARAMS' ? textBoxDoc[fieldKey] : '');
+        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? '';
         const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(':=') || dashVal === '' ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
         return { boolVal: Cast(fval, 'boolean', null), strVal: Field.toString(fval as Field) || '' };
     }
@@ -223,7 +223,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                         Doc.SetInPlace(this._dashDoc!, this._fieldKey, Number(newText), true);
                     } else {
                         const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
-                        if (this._fieldKey !== 'PARAMS' || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
+                        if (!this._textBoxDoc[this._fieldKey]) {
                             const strVal = splits.length > 1 ? new List<string>(splits) : newText;
                             if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
                             Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 361e000f9..809315963 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -17,7 +17,7 @@ import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from '../../../../fields/RichTextField';
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
-import { ComputedField } from '../../../../fields/ScriptField';
+import { ComputedField, ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
@@ -1933,7 +1933,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                                 paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
                                 paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
                                 paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
-                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? 'none' : undefined) : undefined,
+                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (ScriptCast(this.layoutDoc.onClick)?.script.originalScript.includes('followLink(') ? 'none' : undefined) : undefined,
                             }}
                         />
                     </div>
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 9a74b5dba..92696240b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -97,7 +97,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? null : (
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
                 <DocumentView
-                    Document={this.rootDoc}
+                    Document={PresBox.targetRenderedDoc(this.rootDoc)}
                     DataDoc={undefined} //this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 1db56cbdd..3a5ba4f75 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -927,7 +927,7 @@ export namespace Doc {
     //   layout_mytemplate(somparam=somearg).
     // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument
     export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) {
-        const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace('()', '') || StrCast(templateLayoutDoc.PARAMS);
+        const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace('()', '') || '';
         if ((!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc)) || !targetDoc) return templateLayoutDoc;
 
         const templateField = StrCast(templateLayoutDoc.isTemplateForField); // the field that the template renders
@@ -936,7 +936,7 @@ export namespace Doc {
         // If it doesn't find the expanded layout, then it makes a delegate of the template layout and
         // saves it on the data doc indexed by the template layout's id.
         //
-        const params = args.split('=').length > 1 ? args.split('=')[0] : 'PARAMS';
+        const params = args.split('=').length > 1 ? args.split('=')[0] : '';
         const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc);
         const expandedLayoutFieldKey = (templateField || layoutFielddKey) + '-layout[' + templateLayoutDoc[Id] + (args ? `(${args})` : '') + ']';
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
@@ -945,7 +945,7 @@ export namespace Doc {
             expandedTemplateLayout = undefined;
             _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
         } else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) {
-            if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) {
+            if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc))) {
                 expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
             } else {
                 templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
@@ -981,8 +981,8 @@ export namespace Doc {
             console.log('No, no, no!');
             return { layout: childDoc, data: childDoc };
         }
-        const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc;
-        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, '(' + StrCast(containerDoc.PARAMS) + ')'), data: resolvedDataDoc };
+        const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc;
+        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, '()'), data: resolvedDataDoc };
     }
 
     export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 5b489a96c..b7fd06973 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -89,7 +89,6 @@ export const documentSchema = createSchema({
     hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
     linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
     isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations
-    isLinkButton: 'boolean', // whether document functions as a link follow button to follow the first link on the document when clicked
     layers: listSpec('string'), // which layers the document is part of
     _lockedPosition: 'boolean', // whether the document can be moved (dragged)
     _lockedTransform: 'boolean', // whether a freeformview can pan/zoom
-- 
cgit v1.2.3-70-g09d2


From 5d1e3710a015d8915bd367ece753817d84d9d916 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 11 Apr 2023 14:43:54 -0400
Subject: exracted FollowLinkScript() function.  got rid of template parameters
 to simplify templating.

---
 src/client/documents/Documents.ts                  |  5 +--
 src/client/util/CurrentUserUtils.ts                |  3 +-
 src/client/util/LinkFollower.ts                    | 22 ++++++++----
 src/client/views/DocumentButtonBar.tsx             | 13 ++++---
 src/client/views/DocumentDecorations.tsx           |  6 ++--
 src/client/views/MarqueeAnnotator.tsx              |  3 +-
 src/client/views/PropertiesButtons.tsx             |  5 +--
 src/client/views/StyleProvider.tsx                 | 12 +++----
 .../collections/CollectionStackedTimeline.tsx      |  6 ++--
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  8 ++---
 src/client/views/nodes/VideoBox.tsx                |  5 +--
 .../views/nodes/formattedText/DashDocView.tsx      |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 +-
 src/fields/Doc.ts                                  | 41 +++++++---------------
 15 files changed, 65 insertions(+), 71 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7600fcac9..8d97ce5af 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -21,6 +21,7 @@ import { Networking } from '../Network';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager, dropActionType } from '../util/DragManager';
 import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox';
+import { FollowLinkScript } from '../util/LinkFollower';
 import { LinkManager } from '../util/LinkManager';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { undoBatch, UndoManager } from '../util/UndoManager';
@@ -507,7 +508,7 @@ export namespace Docs {
                     layout: { view: LinkBox, dataField: defaultDataKey },
                     options: {
                         childDontRegisterViews: true,
-                        onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
+                        onClick: FollowLinkScript(),
                         hideLinkAnchors: true,
                         _height: 150,
                         description: '',
@@ -1694,7 +1695,7 @@ export namespace DocUtils {
                 _width: 15,
                 _height: 15,
                 _xPadding: 0,
-                onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
+                onClick: FollowLinkScript(),
                 _timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
             });
             Doc.AddDocToList(context, annotationField, pushpin);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 3d0db04b4..76ea3e3ea 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -25,6 +25,7 @@ import { OpenWhere } from "../views/nodes/DocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DragManager } from "./DragManager";
 import { MakeTemplate } from "./DropConverter";
+import { FollowLinkScript } from "./LinkFollower";
 import { LinkManager } from "./LinkManager";
 import { ScriptingGlobals } from "./ScriptingGlobals";
 import { ColorScheme } from "./SettingsManager";
@@ -271,7 +272,7 @@ export class CurrentUserUtils {
             {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _fitWidth: true, _showSidebar: true,  }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
             {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
-            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: ScriptField.MakeScript("followLink(this,altKey)", {altKey:"boolean"}) }},
+            {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: FollowLinkScript()}},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
             {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, treeViewHideUnrendered: true}},
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index f5aae6632..d4d7c66f5 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,9 +1,9 @@
-import { action, runInAction } from 'mobx';
-import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
+import { action, observable, runInAction } from 'mobx';
+import { Doc, DocListCast, Field, FieldResult, Opt } from '../../fields/Doc';
+import { ScriptField } from '../../fields/ScriptField';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
-import { DocumentDecorations } from '../views/DocumentDecorations';
-import { DocFocusOptions, DocumentViewSharedProps, OpenWhere } from '../views/nodes/DocumentView';
+import { DocFocusOptions, OpenWhere } from '../views/nodes/DocumentView';
 import { PresBox } from '../views/nodes/trails';
 import { DocumentManager } from './DocumentManager';
 import { LinkManager } from './LinkManager';
@@ -23,18 +23,19 @@ import { UndoManager } from './UndoManager';
  * - user defined kvps
  */
 export class LinkFollower {
+    @observable public static IsFollowing = false;
     // follows a link - if the target is on screen, it highlights/pans to it.
     // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
     // depending on the followLinkLocation property of the source (or the link itself as a fallback);
     public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, altKey: boolean) => {
         const batch = UndoManager.StartBatch('follow link click');
-        runInAction(() => (DocumentDecorations.Instance.overrideBounds = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value
+        runInAction(() => (LinkFollower.IsFollowing = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value
         LinkFollower.traverseLink(
             linkDoc,
             sourceDoc,
             action(() => {
                 batch.end();
-                Doc.AddUnHighlightWatcher(action(() => (DocumentDecorations.Instance.overrideBounds = false)));
+                Doc.AddUnHighlightWatcher(action(() => (LinkFollower.IsFollowing = false)));
             }),
             altKey ? true : undefined
         );
@@ -124,3 +125,10 @@ ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) {
     SelectionManager.DeselectAll();
     LinkFollower.FollowLink(undefined, doc, altKey);
 });
+
+export function FollowLinkScript() {
+    return ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
+}
+export function IsFollowLinkScript(field: FieldResult<Field>) {
+    return ScriptCast(field)?.script.originalScript.includes('followLink(');
+}
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 7e1b32398..8c2ced55a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,16 +1,17 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction, trace } from 'mobx';
+import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { Cast, DocCast, NumCast, ScriptCast } from '../../fields/Types';
+import { Cast, DocCast, NumCast } from '../../fields/Types';
 import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { DragManager } from '../util/DragManager';
+import { IsFollowLinkScript } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
 import { SharingManager } from '../util/SharingManager';
 import { undoBatch, UndoManager } from '../util/UndoManager';
@@ -24,11 +25,9 @@ import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
 import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
+import { PinProps } from './nodes/trails';
 import { TemplateMenu } from './TemplateMenu';
 import React = require('react');
-import { DocumentType } from '../documents/DocumentTypes';
-import { FontIconBox } from './nodes/button/FontIconBox';
-import { PinProps } from './nodes/trails';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -238,7 +237,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                 </Tooltip>
             );
         };
-        const followLink = ScriptCast(targetDoc?.onClick)?.script.originalScript.includes('followLink(');
+        const followLink = IsFollowLinkScript(targetDoc?.onClick);
         return !targetDoc ? null : (
             <Tooltip title={<div className="dash-tooltip">Set onClick to follow primary link</div>}>
                 <div
@@ -594,7 +593,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         <LinkPopup
                             key="popup"
                             showPopup={this._showLinkPopup}
-                            linkCreated={link => (link.linkDisplay = !ScriptCast(this.props.views().lastElement()?.rootDoc.onClick)?.script.originalScript.includes('followLink('))}
+                            linkCreated={link => (link.linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
                             linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
                             linkFrom={() => this.props.views().lastElement()?.rootDoc}
                         />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9bc583ce5..9ffbe083f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -32,6 +32,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
 import { RichTextField } from '../../fields/RichTextField';
+import { LinkFollower } from '../util/LinkFollower';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -83,10 +84,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         );
     }
 
-    @observable overrideBounds = false;
     @computed
     get Bounds() {
-        if (this.overrideBounds) return { x: 0, y: 0, r: 0, b: 0 };
+        if (LinkFollower.IsFollowing) return { x: 0, y: 0, r: 0, b: 0 };
         const views = SelectionManager.Views();
         return views
             .filter(dv => dv.props.renderDepth > 0)
@@ -908,7 +908,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     {this._isRotating ? null : (
                                         <Tooltip enterDelay={750} title={<div className="dash-tooltip">tap to set rotate center, drag to rotate</div>}>
                                             <div className="documentDecorations-rotation" style={{ pointerEvents: 'all' }} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}>
-                                                <IconButton icon={<FaUndo />} isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} />
+                                                <IconButton icon={<FaUndo />} color={Colors.LIGHT_GRAY} />
                                             </div>
                                         </Tooltip>
                                     )}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 6b4f8ad85..ede387927 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -15,6 +15,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { AnchorMenu } from './pdf/AnchorMenu';
 import React = require('react');
 import { ScriptField } from '../../fields/ScriptField';
+import { FollowLinkScript } from '../util/LinkFollower';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
@@ -148,7 +149,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             const anno = savedAnnos[0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
             const marqueeAnno = Docs.Create.FreeformDocument([], {
-                onClick: isLinkButton ? ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }) : undefined,
+                onClick: isLinkButton ? FollowLinkScript() : undefined,
                 backgroundColor: color,
                 annotationOn: this.props.rootDoc,
                 title: 'Annotation on ' + this.props.rootDoc.title,
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 0723966e1..031d501ad 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -21,6 +21,7 @@ import { DocumentView, OpenWhere } from './nodes/DocumentView';
 import { pasteImageBitmap } from './nodes/WebBoxRenderer';
 import './PropertiesButtons.scss';
 import React = require('react');
+import { IsFollowLinkScript } from '../util/LinkFollower';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -289,7 +290,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             .filter(dv => dv.docView)
             .map(dv => dv.docView!)
             .forEach(docView => {
-                const linkButton = ScriptCast(docView.props.Document.onClick)?.script.originalScript.includes('followLink(');
+                const linkButton = IsFollowLinkScript(docView.props.Document.onClick);
                 docView.noOnClick();
                 switch (onClick) {
                     case 'enterPortal':
@@ -327,7 +328,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         ];
         const list = buttonList.map(value => {
             const click = () => this.handleOptionChange(value[0]);
-            const linkButton = BoolCast(ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('followLink('));
+            const linkButton = IsFollowLinkScript(this.selectedDoc.onClick);
             const followLoc = this.selectedDoc._followLinkLocation;
             const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox);
 
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 8d67f0528..b950b4860 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -1,13 +1,15 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
 import { Shadows } from 'browndash-components';
 import { action, runInAction } from 'mobx';
 import { extname } from 'path';
 import { Doc, Opt, StrListCast } from '../../fields/Doc';
-import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types';
 import { DashColor, lightOrDark, Utils } from '../../Utils';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager';
+import { IsFollowLinkScript } from '../util/LinkFollower';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { ColorScheme, SettingsManager } from '../util/SettingsManager';
@@ -18,13 +20,10 @@ import { InkingStroke } from './InkingStroke';
 import { MainView } from './MainView';
 import { DocumentViewProps } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
+import { KeyValueBox } from './nodes/KeyValueBox';
 import { SliderBox } from './nodes/SliderBox';
 import './StyleProvider.scss';
 import React = require('react');
-import { KeyValueBox } from './nodes/KeyValueBox';
-import { listSpec } from '../../fields/Schema';
-import { AudioField } from '../../fields/URLField';
-import { Tooltip } from '@material-ui/core';
 
 export enum StyleProp {
     TreeViewIcon = 'treeViewIcon',
@@ -279,8 +278,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.BoxShadow: {
             if (!doc || opacity() === 0 || doc.noShadow) return undefined; // if it's not visible, then no shadow)
             if (doc.boxShadow === 'standard') return Shadows.STANDARD_SHADOW;
-            if (ScriptCast(doc?.onClick)?.script.originalScript.includes('followLink(') && LinkManager.Links(doc).length && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any))
-                return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em');
+            if (IsFollowLinkScript(doc?.onClick) && LinkManager.Links(doc).length && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em');
             switch (doc?.type) {
                 case DocumentType.COL:
                     return StrCast(
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 5891cfe78..bbd81d06d 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -14,7 +14,7 @@ import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
-import { LinkFollower } from '../../util/LinkFollower';
+import { FollowLinkScript, IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -406,7 +406,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
                 useLinkSmallAnchor: true,
                 hideLinkButton: true,
-                onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
+                onClick: FollowLinkScript(),
                 annotationOn: rootDoc,
                 _timelineLabel: true,
                 borderRounding: anchorEndTime === undefined ? '100%' : undefined,
@@ -450,7 +450,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     @action
     clickAnchor = (anchorDoc: Doc, clientX: number) => {
-        if (ScriptCast(anchorDoc.onClick)?.script.originalScript.includes('followLink(')) {
+        if (IsFollowLinkScript(anchorDoc.onClick)) {
             LinkFollower.FollowLink(undefined, anchorDoc, false);
         }
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2d12005fb..dbcfe43cf 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -146,7 +146,7 @@ export class DocumentContentsView extends React.Component<
             (this.props.LayoutTemplateString && this.props.Document) ||
             (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
             Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        return Doc.expandTemplateLayout(template, this.props.Document, this.props.layoutKey);
+        return Doc.expandTemplateLayout(template, this.props.Document);
     }
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a39c3cf8f..f161a7b9b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -22,7 +22,7 @@ import { DictationManager } from '../../util/DictationManager';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
-import { LinkFollower } from '../../util/LinkFollower';
+import { FollowLinkScript, LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -560,14 +560,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => {
         const hadOnClick = this.rootDoc.onClick;
         this.noOnClick();
-        this.Document.onClick = hadOnClick ? undefined : ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
+        this.Document.onClick = hadOnClick ? undefined : FollowLinkScript();
         this.Document.waitForDoubleClickToClick = hadOnClick ? undefined : 'never';
     };
     @undoBatch
     @action
     followLinkOnClick = (): void => {
         this.Document.ignoreClick = false;
-        this.Document.onClick = ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
+        this.Document.onClick = FollowLinkScript();
         this.Document.followLinkToggle = false;
         this.Document.followLinkZoom = false;
         this.Document.followLinkLocation = undefined;
@@ -623,7 +623,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             );
         }
         this.Document.followLinkLocation = OpenWhere.lightbox;
-        this.Document.onClick = ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' });
+        this.Document.onClick = FollowLinkScript();
     };
 
     importDocument = () => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 83ad5628c..b144c9318 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -34,6 +34,7 @@ import { RecordingBox } from './RecordingBox';
 import { PinProps, PresBox } from './trails';
 import './VideoBox.scss';
 import { ScriptField } from '../../../fields/ScriptField';
+import { FollowLinkScript } from '../../util/LinkFollower';
 const path = require('path');
 
 /**
@@ -325,7 +326,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 _width: 150,
                 _height: 50,
                 title: (this.layoutDoc._currentTimecode || 0).toString(),
-                onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
+                onClick: FollowLinkScript(),
             });
             this.props.addDocument?.(b);
             DocUtils.MakeLink(b, this.rootDoc, { linkRelationship: 'video snapshot' });
@@ -369,7 +370,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             _nativeHeight: Doc.NativeHeight(this.layoutDoc),
             x: NumCast(this.layoutDoc.x) + width,
             y: NumCast(this.layoutDoc.y),
-            onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }),
+            onClick: FollowLinkScript(),
             _width: 150,
             _height: (height / width) * 150,
             title: '--snapshot' + NumCast(this.layoutDoc._currentTimecode) + ' image-',
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 61345f891..c00ab6a7e 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -77,7 +77,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
 
     updateDoc = action((dashDoc: Doc) => {
         this._dashDoc = dashDoc;
-        this._finalLayout = this.props.docId ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc, this.props.fieldKey);
+        this._finalLayout = this.props.docId ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc);
 
         if (this._finalLayout) {
             if (!Doc.AreProtosEqual(this._finalLayout, dashDoc)) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 809315963..677c4662b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -64,6 +64,7 @@ import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
 import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
+import { IsFollowLinkScript } from '../../../util/LinkFollower';
 const translateGoogleApi = require('translate-google-api');
 
 export const GoogleRef = 'googleDocId';
@@ -1933,7 +1934,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                                 paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
                                 paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
                                 paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
-                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (ScriptCast(this.layoutDoc.onClick)?.script.originalScript.includes('followLink(') ? 'none' : undefined) : undefined,
+                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined) : undefined,
                             }}
                         />
                     </div>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 3a5ba4f75..662792ff0 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -906,57 +906,40 @@ export namespace Doc {
             saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title?
         });
     }
-    //
-    // Determines whether the layout needs to be expanded (as a template).
-    // template expansion is rquired when the layout is a template doc/field and there's a datadoc which isn't equal to the layout template
-    //
-    export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) {
-        return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc;
-    }
 
     const _pendingMap: Map<string, boolean> = new Map();
     //
     // Returns an expanded template layout for a target data document if there is a template relationship
     // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties
     // of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
-    // templateArgs should be equivalent to the layout key that generates the template since that's where the template parameters are stored in ()'s at the end of the key.
-    // NOTE:  the template will have references to "@params" -- the template arguments will be assigned to the '@params' field
-    // so that when the @params key is accessed, it will be rewritten as the key that is stored in the 'params' field and
-    // the derefence will then occur on the rootDocument (the original document).
-    // in the future, field references could be written as @<someparam> and then arguments would be passed in the layout key as:
-    //   layout_mytemplate(somparam=somearg).
-    // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument
-    export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) {
-        const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace('()', '') || '';
-        if ((!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc)) || !targetDoc) return templateLayoutDoc;
-
-        const templateField = StrCast(templateLayoutDoc.isTemplateForField); // the field that the template renders
+    export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc) {
+        // nothing to do if the layout isn't a template or we don't have a target that's different than the template
+        if (!targetDoc || templateLayoutDoc === targetDoc || (!templateLayoutDoc.isTemplateForField && !templateLayoutDoc.isTemplateDoc)) {
+            return templateLayoutDoc;
+        }
+
+        const templateField = StrCast(templateLayoutDoc.isTemplateForField, Doc.LayoutFieldKey(templateLayoutDoc)); // the field that the template renders
         // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc
         // using the template layout doc's id as the field key.
         // If it doesn't find the expanded layout, then it makes a delegate of the template layout and
         // saves it on the data doc indexed by the template layout's id.
         //
-        const params = args.split('=').length > 1 ? args.split('=')[0] : '';
-        const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc);
-        const expandedLayoutFieldKey = (templateField || layoutFielddKey) + '-layout[' + templateLayoutDoc[Id] + (args ? `(${args})` : '') + ']';
+        const expandedLayoutFieldKey = templateField + '-layout[' + templateLayoutDoc[Id] + ']';
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
 
         if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
             expandedTemplateLayout = undefined;
             _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
-        } else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) {
+        } else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey)) {
             if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc))) {
                 expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
             } else {
                 templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
                 if (!targetDoc[expandedLayoutFieldKey]) {
-                    _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true);
+                    _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
                     setTimeout(
                         action(() => {
                             const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, '[' + templateLayoutDoc.title + ']');
-                            // the template's arguments are stored in params which is derefenced to find
-                            // the actual field key where the parameterized template data is stored.
-                            newLayoutDoc[params] = args !== '...' ? args : ''; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params.  it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances
                             newLayoutDoc.rootDocument = targetDoc;
                             const dataDoc = Doc.GetProto(targetDoc);
                             newLayoutDoc.resolvedDataDoc = dataDoc;
@@ -965,7 +948,7 @@ export namespace Doc {
                             }
                             targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
 
-                            _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args);
+                            _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey);
                         })
                     );
                 }
@@ -982,7 +965,7 @@ export namespace Doc {
             return { layout: childDoc, data: childDoc };
         }
         const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc;
-        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, '()'), data: resolvedDataDoc };
+        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc), data: resolvedDataDoc };
     }
 
     export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
-- 
cgit v1.2.3-70-g09d2


From 7342eb81f241ce0b2a5a33ddfb0c865eab6a492f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 27 Apr 2023 09:10:36 -0400
Subject: added proper scaling of multi-selections and groups.  fixed
 pinViewport to work again.

---
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/views/DocumentButtonBar.tsx             |   4 -
 src/client/views/DocumentDecorations.tsx           | 112 ++++++++++++---------
 src/client/views/collections/TabDocView.tsx        |   1 +
 .../collections/collectionFreeForm/MarqueeView.tsx |   5 +-
 src/client/views/nodes/DocumentView.tsx            |  10 +-
 src/client/views/nodes/button/FontIconBox.tsx      |   6 +-
 .../views/nodes/formattedText/RichTextRules.ts     |   2 +-
 src/client/views/nodes/formattedText/marks_rts.ts  |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |   1 +
 10 files changed, 80 insertions(+), 65 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 5475873a9..479fcd011 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -621,8 +621,8 @@ export class CurrentUserUtils {
     }
     static viewTools(): Button[] {
         return [
-            { title: "Grid",          icon: "border-all",   toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Snap\xA0Lines", icon: "th",           toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Grid",          icon: "border-all",   toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "View\xA0All",   icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Clusters",      icon: "braille",      toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Arrange",icon: "arrow-down-short-wide",toolTip: "Toggle Auto Arrange",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange",    funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 8c2ced55a..30e41b06c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -303,10 +303,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             onPointerEnter={action(e => (this.subEndLink = (pinLayout ? 'Layout' : '') + (pinLayout && pinContent ? ' &' : '') + (pinContent ? ' Content' : '')))}
                             onPointerLeave={action(e => (this.subEndLink = ''))}
                             onClick={e => {
-                                const docs = this.props
-                                    .views()
-                                    .filter(v => v)
-                                    .map(dv => dv!.rootDoc);
                                 this.view0 &&
                                     DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.view0.props.Document, true, this.view0, {
                                         pinDocLayout: pinLayout,
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 042e8f6d0..2811c96eb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -12,7 +12,7 @@ import { InkField } from '../../fields/InkField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
 import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
+import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DragManager } from '../util/DragManager';
@@ -34,6 +34,7 @@ import React = require('react');
 import { RichTextField } from '../../fields/RichTextField';
 import { LinkFollower } from '../util/LinkFollower';
 import _ = require('lodash');
+import { DocumentManager } from '../util/DocumentManager';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -509,73 +510,86 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             dragRight = false,
             dragBotRight = false,
             dragTop = false;
-        let dX = 0,
-            dY = 0,
-            dW = 0,
-            dH = 0;
+        let dXin = 0,
+            dYin = 0,
+            dWin = 0,
+            dHin = 0;
         switch (this._resizeHdlId.split(' ')[0]) {
             case '':
                 break;
             case 'documentDecorations-topLeftResizer':
-                dX = -1;
-                dY = -1;
-                dW = -move[0];
-                dH = -move[1];
+                dXin = -1;
+                dYin = -1;
+                dWin = -move[0];
+                dHin = -move[1];
                 break;
             case 'documentDecorations-topRightResizer':
-                dW = move[0];
-                dY = -1;
-                dH = -move[1];
+                dWin = move[0];
+                dYin = -1;
+                dHin = -move[1];
                 break;
             case 'documentDecorations-topResizer':
-                dY = -1;
-                dH = -move[1];
+                dYin = -1;
+                dHin = -move[1];
                 dragTop = true;
                 break;
             case 'documentDecorations-bottomLeftResizer':
-                dX = -1;
-                dW = -move[0];
-                dH = move[1];
+                dXin = -1;
+                dWin = -move[0];
+                dHin = move[1];
                 break;
             case 'documentDecorations-bottomRightResizer':
-                dW = move[0];
-                dH = move[1];
+                dWin = move[0];
+                dHin = move[1];
                 dragBotRight = true;
                 break;
             case 'documentDecorations-bottomResizer':
-                dH = move[1];
+                dHin = move[1];
                 dragBottom = true;
                 break;
             case 'documentDecorations-leftResizer':
-                dX = -1;
-                dW = -move[0];
+                dXin = -1;
+                dWin = -move[0];
                 break;
             case 'documentDecorations-rightResizer':
-                dW = move[0];
+                dWin = move[0];
                 dragRight = true;
                 break;
         }
 
-        SelectionManager.Views().forEach(
+        const isGroup = first.rootDoc._isGroup ? first.rootDoc : undefined;
+        const scaleViews = isGroup ? DocListCast(isGroup.data).map(doc => DocumentManager.Instance.getFirstDocumentView(doc)!) : SelectionManager.Views();
+        const aggBounds = aggregateBounds(scaleViews.map(view => view.rootDoc) as any, 0, 0);
+        const refWidth = aggBounds.r - aggBounds.x;
+        const refHeight = aggBounds.b - aggBounds.y;
+        const scaleRefPt = first.props
+            .ScreenToLocalTransform()
+            .inverse()
+            .transformPoint(
+                NumCast(isGroup?._xPadding) + (dXin ? refWidth : 0), //
+                NumCast(isGroup?._yPadding) + (dYin ? refHeight : 0)
+            );
+        scaleViews.forEach(
             action((docView: DocumentView) => {
                 if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
-                if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                    const doc = Document(docView.rootDoc);
+                if (dXin !== 0 || dYin !== 0 || dWin !== 0 || dHin !== 0) {
+                    const doc = docView.rootDoc;
+                    const refCent = docView.props.ScreenToLocalTransform().transformPoint(scaleRefPt[0], scaleRefPt[1]);
+
                     if (doc.nativeHeightUnfrozen && !NumCast(doc.nativeHeight)) {
                         doc._nativeHeight = (NumCast(doc._height) / NumCast(doc._width, 1)) * docView.nativeWidth;
                     }
                     const nwidth = docView.nativeWidth;
                     const nheight = docView.nativeHeight;
-                    let docheight = doc._height || 0;
-                    let docwidth = doc._width || 0;
-                    const width = docwidth;
-                    let height = docheight || (nheight / nwidth) * width;
-                    height = !height || isNaN(height) ? 20 : height;
+                    const docwidth = NumCast(doc._width);
+                    let docheight = (hgt => (!hgt || isNaN(hgt) ? 20 : hgt))(NumCast(doc._height) || (nheight / nwidth) * docwidth);
+                    let dW = docwidth * (dWin / refWidth);
+                    let dH = docheight * (dHin / refHeight);
                     const scale = docView.props.ScreenToLocalTransform().Scale;
                     const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
                     if (nwidth && nheight) {
-                        if (nwidth / nheight !== width / height && !dragBottom && !dragTop) {
-                            height = (nheight / nwidth) * width;
+                        if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) {
+                            docheight = (nheight / nwidth) * docwidth;
                         }
                         if (modifyNativeDim && !dragBottom && !dragTop) {
                             // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
@@ -583,21 +597,25 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             else dW = (dH * nwidth) / nheight;
                         }
                     }
-                    let actualdW = Math.max(width + dW * scale, 20);
-                    let actualdH = Math.max(height + dH * scale, 20);
+                    let actualdW = Math.max(docwidth + dW * scale, 20);
+                    let actualdH = Math.max(docheight + dH * scale, 20);
+                    let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
+                    let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
                     const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false;
                     const fixedAspect = nwidth && nheight && (!doc._fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
                     if (fixedAspect) {
                         if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) {
                             if (dragRight && modifyNativeDim) {
                                 if (Doc.NativeWidth(doc)) {
-                                    doc._nativeWidth = (actualdW / (doc._width || 1)) * Doc.NativeWidth(doc);
+                                    doc._nativeWidth = (actualdW / (docwidth || 1)) * Doc.NativeWidth(doc);
                                 }
                             } else {
                                 if (!doc._fitWidth || preserveNativeDim) {
                                     actualdH = (nheight / nwidth) * actualdW;
                                     doc._height = actualdH;
-                                } else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
+                                } else if (!modifyNativeDim || dragBotRight) {
+                                    doc._height = actualdH;
+                                }
                             }
                             doc._width = actualdW;
                         } else {
@@ -605,21 +623,23 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight
                                 // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match
                                 // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
-                                doc._nativeHeight = (actualdH / (doc._height || 1)) * Doc.NativeHeight(doc);
+                                doc._nativeHeight = (actualdH / (docheight || 1)) * Doc.NativeHeight(doc);
                                 doc._autoHeight = false;
                             } else {
                                 if (!doc._fitWidth || preserveNativeDim) {
                                     actualdW = (nwidth / nheight) * actualdH;
                                     doc._width = actualdW;
-                                } else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
+                                } else if (!modifyNativeDim || dragBotRight) {
+                                    doc._width = actualdW;
+                                }
                             }
                             if (!modifyNativeDim) {
-                                actualdH = Math.min((nheight / nwidth) * NumCast(doc._width), actualdH);
-                                doc._height = actualdH;
-                            } else doc._height = actualdH;
+                                actualdH = Math.min((nheight / nwidth) * docwidth, actualdH);
+                            }
+                            doc._height = actualdH;
                         }
                     } else {
-                        const rotCtr = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
+                        const rotCtr = [docwidth / 2, docheight / 2];
                         const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
 
                         const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
@@ -632,8 +652,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         doc.x = NumCast(doc.x) + tlRotated.x + rotCtr[0] - (tlRotated2.x + rotCtr2[0]); // doc shifts by amount topleft moves because rotation is about center of doc
                         doc.y = NumCast(doc.y) + tlRotated.y + rotCtr[1] - (tlRotated2.y + rotCtr2[1]);
                     }
-                    doc.x = (doc.x || 0) + dX * (actualdW - docwidth);
-                    doc.y = (doc.y || 0) + (dragBottom ? 0 : dY * (actualdH - docheight));
+                    doc.x = NumCast(doc.x) + dX;
+                    doc.y = NumCast(doc.y) + dY;
                     doc._lastModified = new DateField();
                 }
                 const val = this._dragHeights.get(docView.layoutDoc);
@@ -726,7 +746,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
-        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating;
+        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || this._isRounding || this._isRotating;
         const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
         const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 4bbc3bb44..45604c1bf 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -266,6 +266,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
             const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
 
+            if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = 'manual';
                 pinDoc.mediaStop = 'manual';
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index eaeb5f933..e5f47823c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -425,9 +425,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
      */
     @undoBatch
     @action
-    pinWithView = async () => {
-        const doc = this.props.Document;
-        TabDocView.PinDoc(doc, { pinViewport: this.Bounds });
+    pinWithView = () => {
+        TabDocView.PinDoc(this.props.Document, { pinViewport: this.Bounds });
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
     };
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c957bc778..a25e5c42d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -765,9 +765,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
         }
         const constantItems: ContextMenuProps[] = [];
-
+        constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' });
         if (!Doc.IsSystem(this.rootDoc)) {
-            constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' });
             constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
             constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() });
             (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
@@ -775,8 +774,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
                 constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
             }
-            cm.addItem({ description: 'General...', noexpand: true, subitems: constantItems, icon: 'question' });
         }
+        cm.addItem({ description: 'General...', noexpand: !Doc.IsSystem(this.rootDoc), subitems: constantItems, icon: 'question' });
+
         const help = cm.findByDescription('Help...');
         const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
         !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' });
@@ -819,9 +819,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (documentationDescription && documentationLink) {
             helpItems.push({
                 description: documentationDescription,
-                event: () => {
-                    window.open(documentationLink, '_blank');
-                },
+                event: () => window.open(documentationLink, '_blank'),
                 icon: 'book',
             });
         }
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 1a75a7e76..4f570b5fc 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -610,16 +610,16 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
 });
 
-ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snap lines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
+ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snapline' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     // prettier-ignore
-    const map: Map<'grid' | 'snap lines' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+    const map: Map<'grid' | 'snapline' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
         ['grid', {
             undo: false,
             checkResult: (doc:Doc) => doc._backgroundGridShow,
             setDoc: (doc:Doc) => doc._backgroundGridShow = !doc._backgroundGridShow,
         }],
-        ['snap lines', {
+        ['snapline', {
             undo: false,
             checkResult: (doc:Doc) => doc.showSnapLines,
             setDoc: (doc:Doc) => doc._showSnapLines = !doc._showSnapLines,
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 20ce929ad..e5943f257 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -391,7 +391,7 @@ export class RichTextRules {
                 const content = selected.selection.content();
                 const replaced = node ? selected.replaceRangeWith(start, end, schema.nodes.summary.create({ visibility: true, text: content, textslice: content.toJSON() })) : state.tr;
 
-                return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1))).setStoredMarks([...node.marks, ...sm]);
+                return replaced.setSelection(new TextSelection(replaced.doc.resolve(end))).setStoredMarks([...node.marks, ...sm]);
             }),
 
             new InputRule(new RegExp(/%\)/), (state, match, start, end) => {
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 3898490d3..5b47e8a70 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -349,7 +349,7 @@ export const marks: { [index: string]: MarkSpec } = {
         group: 'inline',
         toDOM(node: any) {
             const uid = node.attrs.userid.replace('.', '').replace('@', '');
-            const min = Math.round(node.attrs.modified / 12);
+            const min = Math.round(node.attrs.modified / 60);
             const hr = Math.round(min / 60);
             const day = Math.round(hr / 60 / 24);
             const remote = node.attrs.userid !== Doc.CurrentUserEmail ? ' UM-remote' : '';
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index f8c47aafe..0b780f589 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -567,6 +567,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 bestTarget._panY = viewport.panY;
                 const dv = DocumentManager.Instance.getDocumentView(bestTarget);
                 if (dv) {
+                    changed = true;
                     const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
                     activeItem.presMovement === PresMovement.Zoom && (bestTarget._viewScale = computedScale);
                     dv.ComponentView?.brushView?.(viewport);
-- 
cgit v1.2.3-70-g09d2


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

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

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

diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index ff9460b62..e8fd8fb8a 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,27 +1,26 @@
-import { AssertionError } from "assert";
-import { EditorState } from "prosemirror-state";
-import { Doc, DocListCastAsync, Opt } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { RichTextField } from "../../../fields/RichTextField";
-import { RichTextUtils } from "../../../fields/RichTextUtils";
-import { Cast, StrCast } from "../../../fields/Types";
-import { ImageField } from "../../../fields/URLField";
-import { MediaItem, NewMediaItemResult } from "../../../server/apis/google/SharedTypes";
-import { Utils } from "../../../Utils";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
-import { Networking } from "../../Network";
-import { FormattedTextBox } from "../../views/nodes/formattedText/FormattedTextBox";
-import { GoogleAuthenticationManager } from "../GoogleAuthenticationManager";
+import { AssertionError } from 'assert';
+import { EditorState } from 'prosemirror-state';
+import { Doc, DocListCastAsync, Opt } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { RichTextField } from '../../../fields/RichTextField';
+import { RichTextUtils } from '../../../fields/RichTextUtils';
+import { Cast, StrCast } from '../../../fields/Types';
+import { ImageField } from '../../../fields/URLField';
+import { MediaItem, NewMediaItemResult } from '../../../server/apis/google/SharedTypes';
+import { Utils } from '../../../Utils';
+import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
+import { Networking } from '../../Network';
+import { FormattedTextBox } from '../../views/nodes/formattedText/FormattedTextBox';
+import { GoogleAuthenticationManager } from '../GoogleAuthenticationManager';
 import Photos = require('googlephotos');
 
 export namespace GooglePhotos {
-
     const endpoint = async () => new Photos(await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken());
 
     export enum MediaType {
         ALL_MEDIA = 'ALL_MEDIA',
         PHOTO = 'PHOTO',
-        VIDEO = 'VIDEO'
+        VIDEO = 'VIDEO',
     }
 
     export type AlbumReference = { id: string } | { title: string };
@@ -58,11 +57,10 @@ export namespace GooglePhotos {
         HOUSES: 'HOUSES',
         GARDENS: 'GARDENS',
         FLOWERS: 'FLOWERS',
-        HOLIDAYS: 'HOLIDAYS'
+        HOLIDAYS: 'HOLIDAYS',
     };
 
     export namespace Export {
-
         export interface AlbumCreationResult {
             albumId: string;
             mediaItems: MediaItem[];
@@ -82,20 +80,20 @@ export namespace GooglePhotos {
             if (!images || !images.length) {
                 return undefined;
             }
-            const resolved = title ? title : (StrCast(collection.title) || `Dash Collection (${collection[Id]}`);
+            const resolved = title ? title : StrCast(collection.title) || `Dash Collection (${collection[Id]}`;
             const { id, productUrl } = await Create.Album(resolved);
             const response = await Transactions.UploadImages(images, { id }, descriptionKey);
             if (response) {
                 const { results, failed } = response;
                 let index: Opt<number>;
                 while ((index = failed.pop()) !== undefined) {
-                    Doc.RemoveDocFromList(dataDocument, "data", images.splice(index, 1)[0]);
+                    Doc.RemoveDocFromList(dataDocument, 'data', images.splice(index, 1)[0]);
                 }
                 const mediaItems: MediaItem[] = results.map(item => item.mediaItem);
                 if (mediaItems.length !== images.length) {
                     throw new AssertionError({ actual: mediaItems.length, expected: images.length });
                 }
-                const idMapping = new Doc;
+                const idMapping = new Doc();
                 for (let i = 0; i < images.length; i++) {
                     const image = Doc.GetProto(images[i]);
                     const mediaItem = mediaItems[i];
@@ -117,38 +115,30 @@ export namespace GooglePhotos {
                 return { albumId: id, mediaItems };
             }
         };
-
     }
 
     export namespace Import {
-
         export type CollectionConstructor = (data: Array<Doc>, options: DocumentOptions, ...args: any) => Doc;
 
         export const CollectionFromSearch = async (constructor: CollectionConstructor, requested: Opt<Partial<Query.SearchOptions>>): Promise<Doc> => {
             await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
             const response = await Query.ContentSearch(requested);
             const uploads = await Transactions.WriteMediaItemsToServer(response);
-            const children = uploads.map((upload: Transactions.UploadInformation) => {
-                const document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean));
-                document.contentSize = upload.contentSize;
-                return document;
-            });
+            const children = uploads.map((upload: Transactions.UploadInformation) => Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean) /*, {"data_contentSize":upload.contentSize}*/));
             const options = { _width: 500, _height: 500 };
             return constructor(children, options);
         };
-
     }
 
     export namespace Query {
-
-        const delimiter = ", ";
-        const comparator = (a: string, b: string) => (a < b) ? -1 : (a > b ? 1 : 0);
+        const delimiter = ', ';
+        const comparator = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0);
 
         export const TagChildImages = async (collection: Doc) => {
             await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
             const idMapping = await Cast(collection.googlePhotosIdMapping, Doc);
             if (!idMapping) {
-                throw new Error("Appending image metadata requires that the targeted collection have already been mapped to an album!");
+                throw new Error('Appending image metadata requires that the targeted collection have already been mapped to an album!');
             }
             const tagMapping = new Map<string, string>();
             const images = (await DocListCastAsync(collection.data))!.map(Doc.GetProto);
@@ -169,7 +159,7 @@ export namespace GooglePhotos {
                 const concatenated = tagMapping.get(image[Id])!;
                 const tags = concatenated.split(delimiter);
                 if (tags.length > 1) {
-                    const cleaned = concatenated.replace(ContentCategories.NONE + delimiter, "");
+                    const cleaned = concatenated.replace(ContentCategories.NONE + delimiter, '');
                     image.googlePhotosTags = cleaned.split(delimiter).sort(comparator).join(delimiter);
                 } else {
                     image.googlePhotosTags = ContentCategories.NONE;
@@ -253,19 +243,15 @@ export namespace GooglePhotos {
         export const GetImage = async (mediaItemId: string): Promise<Transactions.MediaItem> => {
             return (await endpoint()).mediaItems.get(mediaItemId);
         };
-
     }
 
     namespace Create {
-
         export const Album = async (title: string) => {
             return (await endpoint()).albums.create(title);
         };
-
     }
 
     export namespace Transactions {
-
         export interface UploadInformation {
             mediaPaths: string[];
             fileNames: { [key: string]: string };
@@ -295,18 +281,20 @@ export namespace GooglePhotos {
         };
 
         export const WriteMediaItemsToServer = async (body: { mediaItems: any[] }): Promise<UploadInformation[]> => {
-            const uploads = await Networking.PostToServer("/googlePhotosMediaGet", body);
+            const uploads = await Networking.PostToServer('/googlePhotosMediaGet', body);
             return uploads;
         };
 
-        export const UploadThenFetch = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption") => {
+        export const UploadThenFetch = async (sources: Doc[], album?: AlbumReference, descriptionKey = 'caption') => {
             const response = await UploadImages(sources, album, descriptionKey);
             if (!response) {
                 return undefined;
             }
-            const baseUrls: string[] = await Promise.all(response.results.map(item => {
-                return new Promise<string>(resolve => Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl)));
-            }));
+            const baseUrls: string[] = await Promise.all(
+                response.results.map(item => {
+                    return new Promise<string>(resolve => Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl)));
+                })
+            );
             return baseUrls;
         };
 
@@ -315,9 +303,9 @@ export namespace GooglePhotos {
             failed: number[];
         }
 
-        export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption"): Promise<Opt<ImageUploadResults>> => {
+        export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = 'caption'): Promise<Opt<ImageUploadResults>> => {
             await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
-            if (album && "title" in album) {
+            if (album && 'title' in album) {
                 album = await Create.Album(album.title);
             }
             const media: MediaInput[] = [];
@@ -327,13 +315,13 @@ export namespace GooglePhotos {
                     return;
                 }
                 const url = data.url.href;
-                const target = Doc.MakeAlias(source);
+                const target = Doc.MakeEmbedding(source);
                 const description = parseDescription(target, descriptionKey);
                 await DocUtils.makeCustomViewClicked(target, Docs.Create.FreeformDocument);
                 media.push({ url, description });
             }
             if (media.length) {
-                const results = await Networking.PostToServer("/googlePhotosMediaPost", { media, album });
+                const results = await Networking.PostToServer('/googlePhotosMediaPost', { media, album });
                 return results;
             }
         };
@@ -341,14 +329,12 @@ export namespace GooglePhotos {
         const parseDescription = (document: Doc, descriptionKey: string) => {
             let description: string = Utils.prepend(`/doc/${document[Id]}?sharing=true`);
             const target = document[descriptionKey];
-            if (typeof target === "string") {
+            if (typeof target === 'string') {
                 description = target;
             } else if (target instanceof RichTextField) {
                 description = RichTextUtils.ToPlainText(EditorState.fromJSON(FormattedTextBox.Instance.config, JSON.parse(target.Data)));
             }
             return description;
         };
-
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index d99cd2dac..2da3a24fd 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -11,7 +11,7 @@ export enum DocumentType {
     AUDIO = 'audio',
     REC = 'recording',
     PDF = 'pdf',
-    INK = 'inks',
+    INK = 'ink',
     SCREENSHOT = 'screenshot',
     FONTICON = 'fonticonbox',
     FILTER = 'filter',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e28e29267..849a5d6ae 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -37,7 +37,6 @@ import { FontIconBox } from '../views/nodes/button/FontIconBox';
 import { ColorBox } from '../views/nodes/ColorBox';
 import { ComparisonBox } from '../views/nodes/ComparisonBox';
 import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
-import { OpenWhereMod } from '../views/nodes/DocumentView';
 import { EquationBox } from '../views/nodes/EquationBox';
 import { FieldViewProps } from '../views/nodes/FieldView';
 import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
@@ -120,7 +119,7 @@ class PEInfo extends FInfo {
 }
 class DAInfo extends FInfo {
     fieldType? = 'dropActionType';
-    values? = ['alias', 'copy', 'move', 'same', 'proto', 'none'];
+    values? = ['embed', 'copy', 'move', 'same', 'proto', 'none'];
     readOnly = true;
 }
 class DateInfo extends FInfo {
@@ -139,7 +138,7 @@ export class DocumentOptions {
     x?: NUMt = new NumInfo('x coordinate of document in a freeform view');
     y?: NUMt = new NumInfo('y coordinage of document in a freeform view');
     z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, [1, 0]);
-    system?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
+    isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
     type?: STRt = new StrInfo('type of document', true, Array.from(Object.keys(DocumentType)));
     title?: string;
     creationDate?: DATEt = new DateInfo('date the document was created', true);
@@ -150,33 +149,33 @@ export class DocumentOptions {
     userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
     color?: STRt = new StrInfo('foreground color data doc');
     backgroundColor?: STRt = new StrInfo('background color for data doc');
-    _autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
+    _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
     _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
     _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
     _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
-    _panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
-    _panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
+    _freeform_panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
+    _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
     _width?: NUMt = new NumInfo('displayed width of a document');
     _height?: NUMt = new NumInfo('displayed height of document');
-    'data-nativeWidth'?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)');
-    'data-nativeHeight'?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)');
+    data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)');
+    data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)');
     _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)');
     _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)');
     _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
     _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
-    _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
-    _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
+    _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
+    _layoutFitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
     _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
-    _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
+    _lockedTransform?: boolean; // lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed
     _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target
-    _showTitle?: string; // field name to display in header (:hover is an optional suffix)
-    _showAltContentUI?: boolean; // whether to show alternate content button
+    _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
+    _layout_altContentUI?: boolean; // whether to show alternate content button
     _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target
-    _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
-    _scrollTop?: number; // scroll location for pdfs
+    _layout_showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
+    _layoutScrollTop?: number; // scroll location for pdfs
     _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged
     _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
     _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
@@ -188,15 +187,16 @@ export class DocumentOptions {
     _viewType?: string; // sub type of a collection
     viewType?: string; // sub type of a collection
     _gridGap?: number; // gap between items in masonry view
-    _viewScale?: number; // how much a freeform view has been scaled (zoomed)
+    _freeform_scale?: number; // how much a freeform view has been scaled (zoomed)
     _overflow?: string; // set overflow behavior
     _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts
     _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts
     _xPadding?: number;
     _yPadding?: number;
     _itemIndex?: number; // which item index the carousel viewer is showing
-    _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts
-    _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document)
+    _layout_showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts
+    _singleLine?: boolean; // whether label box is restricted to one line of text
+    _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents
     _minFontSize?: number; // minimum font size for labelBoxes
     _maxFontSize?: number; // maximum font size for labelBoxes
     _columnWidth?: number;
@@ -205,24 +205,24 @@ export class DocumentOptions {
     _fontFamily?: string;
     _fontWeight?: string;
     _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
-    _curPage?: number; // current page of a PDF or other? paginated document
-    _currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video)  value is in seconds
+    _layout_curPage?: number; // current page of a PDF or other? paginated document
+    _layout_currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video)  value is in seconds
     _currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide)
     _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
     _timecodeToHide?: number; // the time that a document should be hidden
     _timelineLabel?: boolean; // whether the document exists on a timeline
-    '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection', true);
-    '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection', true);
-    'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view', true);
-    'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view', true);
-    'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
+    _caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', true);
+    _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', true);
+    icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', true);
+    icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', true);
+    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
     openFactoryAsDelegate?: boolean; //
     lat?: number;
     lng?: number;
     infoWindowOpen?: boolean;
     author?: string;
-    _layoutKey?: string;
+    _layout_fieldKey?: string;
     fieldValues?: List<any>; // possible field values used by fieldInfos
     fieldType?: string; // type of afield used by fieldInfos
     unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)
@@ -249,7 +249,6 @@ export class DocumentOptions {
     defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
     waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
-    description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
     contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
@@ -263,11 +262,11 @@ export class DocumentOptions {
     childContextMenuLabels?: List<string>;
     childContextMenuIcons?: List<string>;
     followLinkZoom?: boolean; // whether to zoom to the target of a link
-    hideLinkButton?: boolean; // whether the blue link counter button should be hidden
+    layout_hideLinkButton?: boolean; // whether the blue link counter button should be hidden
     disableDocBrushing?: boolean; // whether to suppress border highlighting
-    hideDecorationTitle?: boolean;
+    layout_hideDecorationTitle?: boolean;
     hideOpenButton?: boolean;
-    hideResizeHandles?: boolean;
+    layout_hideResizeHandles?: boolean;
     hideDocumentButtonBar?: boolean;
     hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
@@ -278,8 +277,8 @@ export class DocumentOptions {
     caption?: RichTextField;
     opacity?: number;
     defaultBackgroundColor?: string;
-    _linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint
-    hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed
+    _layout_autoMoveAnchors?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint
+    layout_hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed
     isFolder?: boolean;
     lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide)
     activeFrame?: number; // the active frame of a document in a frame base collection
@@ -294,12 +293,11 @@ export class DocumentOptions {
     borderRounding?: string;
     boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
     data?: any;
-    baseProto?: boolean; // is this a base prototoype
+    isBaseProto?: boolean; // this Doc is base level prototype for data documents as opposed to data documents which are prototypes for layout documents.  base protos are not cloned during a deep
     dontRegisterView?: boolean;
     lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document.  see PresBox.
     columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
     schemaHeaders?: List<SchemaHeaderField>; // headers for schema view
-    clipWidth?: number; // percent transition from before to after in comparisonBox
     dockingConfig?: string;
     annotationOn?: Doc;
     followLinkToggle?: boolean;
@@ -307,7 +305,7 @@ export class DocumentOptions {
     _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
     noteType?: string;
     // BACKGROUND GRID
-    _backgroundGridShow?: boolean;
+    _freeform_backgroundGrid?: boolean;
 
     //BUTTONS
     buttonText?: string;
@@ -318,7 +316,6 @@ export class DocumentOptions {
     userColorBtn?: string;
     canClick?: string;
     script?: ScriptField;
-    numBtnType?: string;
     numBtnMax?: number;
     numBtnMin?: number;
     switchToggle?: boolean;
@@ -335,12 +332,14 @@ export class DocumentOptions {
 
     layout_linkView?: Doc; // view template for a link document
     layout_keyValue?: string; // view tempalte for key value docs
-    linkRelationship?: string; // type of relatinoship a link represents
-    linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors
-    anchor1?: Doc;
-    anchor2?: Doc;
-    'anchor1-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
-    'anchor2-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    link_description?: string; // added for links
+    link_relationship?: string; // type of relatinoship a link represents
+    layout_linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors
+    layout_linkDisplayArrow?: boolean; // whether to display link's directional arrowhead
+    link_anchor_1?: Doc;
+    link_anchor_2?: Doc;
+    link_anchor_1_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    link_anchor_2_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
     ignoreClick?: boolean;
     onClick?: ScriptField;
     onDoubleClick?: ScriptField;
@@ -354,13 +353,13 @@ export class DocumentOptions {
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
     filterBoolean?: string;
-    useCors?: boolean;
+    data_useCors?: boolean;
     icon?: string;
     target?: Doc; // available for use in scripts as the primary target document
     sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
     targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
     searchFileTypes?: List<string>; // file types allowed in a search query
-    strokeWidth?: number;
+    stroke_width?: number;
     freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection
     treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
     treeViewHideUnrendered?: boolean; // tells tree view not to display documents that have an 'unrendered' tag unless they also have a treeViewFieldKey tag (presBox)
@@ -395,8 +394,8 @@ export class DocumentOptions {
     useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox
     border?: string; //for searchbox
     hoverBackgroundColor?: string; // background color of a label when hovered
-    linkRelationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
-    linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship
+    link_relationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
+    link_relationshipSizes?: List<number>; //stores number of links contained in each relationship
     linkColorList?: List<string>; // colors of links corresponding to specific link relationships
 }
 export namespace Docs {
@@ -427,7 +426,7 @@ export namespace Docs {
                         _yMargin: 10,
                         nativeDimModifiable: true,
                         nativeHeightUnfrozen: true,
-                        forceReflow: true,
+                        layout_forceReflow: true,
                         defaultDoubleClick: 'ignore',
                     },
                 },
@@ -457,35 +456,35 @@ export namespace Docs {
                 DocumentType.WEB,
                 {
                     layout: { view: WebBox, dataField: defaultDataKey },
-                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
+                    options: { _height: 300, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
                 },
             ],
             [
                 DocumentType.COL,
                 {
                     layout: { view: CollectionView, dataField: defaultDataKey },
-                    options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1 },
+                    options: { _layout_fitWidth: true, _freeform_panX: 0, _freeform_panY: 0, _freeform_scale: 1 },
                 },
             ],
             [
                 DocumentType.KVP,
                 {
                     layout: { view: KeyValueBox, dataField: defaultDataKey },
-                    options: { _fitWidth: true, _height: 150 },
+                    options: { _layout_fitWidth: true, _height: 150 },
                 },
             ],
             [
                 DocumentType.VID,
                 {
                     layout: { view: VideoBox, dataField: defaultDataKey },
-                    options: { _currentTimecode: 0 },
+                    options: { _layout_currentTimecode: 0 },
                 },
             ],
             [
                 DocumentType.AUDIO,
                 {
                     layout: { view: AudioBox, dataField: defaultDataKey },
-                    options: { _height: 100, fitWidth: true, forceReflow: true, nativeDimModifiable: true },
+                    options: { _height: 100, layout_fitWidth: true, layout_forceReflow: true, nativeDimModifiable: true },
                 },
             ],
             [
@@ -499,7 +498,7 @@ export namespace Docs {
                 DocumentType.PDF,
                 {
                     layout: { view: PDFBox, dataField: defaultDataKey },
-                    options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
+                    options: { _layout_curPage: 1, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
                 },
             ],
             [
@@ -519,14 +518,15 @@ export namespace Docs {
             [
                 DocumentType.LINK,
                 {
-                    layout: { view: LinkBox, dataField: defaultDataKey },
+                    layout: { view: LinkBox, dataField: 'link' },
                     options: {
                         childDontRegisterViews: true,
                         onClick: FollowLinkScript(),
-                        hideLinkAnchors: true,
+                        layout_hideLinkAnchors: true,
                         _height: 150,
-                        description: '',
-                        showCaption: 'description',
+                        link: '',
+                        link_description: '',
+                        layout_showCaption: 'link_description',
                         backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
                         _removeDropProperties: new List(['onClick']),
                     },
@@ -537,7 +537,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'alias', title: 'Global Link Database' },
+                    options: { childDropAction: 'embed', title: 'Global Link Database' },
                 },
             ],
             [
@@ -545,7 +545,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'alias', title: 'Global Script Database' },
+                    options: { childDropAction: 'embed', title: 'Global Script Database' },
                 },
             ],
             [
@@ -571,8 +571,8 @@ export namespace Docs {
             [
                 DocumentType.EQUATION,
                 {
-                    layout: { view: EquationBox, dataField: defaultDataKey },
-                    options: { nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true },
+                    layout: { view: EquationBox, dataField: 'text' },
+                    options: { nativeDimModifiable: true, fontSize: '14px', layout_hideResizeHandles: true, layout_hideDecorationTitle: true },
                 },
             ],
             [
@@ -600,14 +600,14 @@ export namespace Docs {
                 DocumentType.PRES,
                 {
                     layout: { view: PresBox, dataField: defaultDataKey },
-                    options: { defaultDoubleClick: 'ignore', hideLinkAnchors: true },
+                    options: { defaultDoubleClick: 'ignore', layout_hideLinkAnchors: true },
                 },
             ],
             [
                 DocumentType.FONTICON,
                 {
                     layout: { view: FontIconBox, dataField: 'icon' },
-                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, hideLinkButton: true, _width: 40, _height: 40 },
+                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, layout_hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
             [
@@ -621,21 +621,21 @@ export namespace Docs {
                 DocumentType.PRESELEMENT,
                 {
                     layout: { view: PresElementBox, dataField: defaultDataKey },
-                    options: { title: 'pres element template', _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' },
+                    options: { title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' },
                 },
             ],
             [
                 DocumentType.MARKER,
                 {
                     layout: { view: CollectionView, dataField: defaultDataKey },
-                    options: { hideLinkButton: true, pointerEvents: 'none' },
+                    options: { layout_hideLinkButton: true, pointerEvents: 'none' },
                 },
             ],
             [
                 DocumentType.INK,
                 {
                     // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
-                    layout: { view: InkingStroke, dataField: defaultDataKey },
+                    layout: { view: InkingStroke, dataField: 'stroke' },
                     options: {},
                 },
             ],
@@ -649,8 +649,9 @@ export namespace Docs {
             [
                 DocumentType.COMPARISON,
                 {
+                    data: '',
                     layout: { view: ComparisonBox, dataField: defaultDataKey },
-                    options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias' },
+                    options: { nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'embed' },
                 },
             ],
             [
@@ -658,7 +659,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'alias', title: 'Global Group Database' },
+                    options: { childDropAction: 'embed', title: 'Global Group Database' },
                 },
             ],
             [
@@ -672,14 +673,14 @@ export namespace Docs {
                 DocumentType.DATAVIZ,
                 {
                     layout: { view: DataVizBox, dataField: defaultDataKey },
-                    options: { _fitWidth: true, nativeDimModifiable: true },
+                    options: { _layout_fitWidth: true, nativeDimModifiable: true },
                 },
             ],
             [
                 DocumentType.LOADING,
                 {
                     layout: { view: LoadingBox, dataField: '' },
-                    options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true },
+                    options: { _layout_fitWidth: true, _fitHeight: true, nativeDimModifiable: true },
                 },
             ],
         ]);
@@ -773,11 +774,11 @@ export namespace Docs {
             // synthesize the default options, the type and title from computed values and
             // whatever options pertain to this specific prototype
             const options: DocumentOptions = {
-                system: true,
-                _layoutKey: 'layout',
+                isSystem: true,
+                _layout_fieldKey: 'layout',
                 title,
                 type,
-                baseProto: true,
+                isBaseProto: true,
                 x: 0,
                 y: 0,
                 _width: 300,
@@ -818,24 +819,24 @@ export namespace Docs {
          * main document.
          */
         function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) {
-            const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_"
+            const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_"
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
             // dataProps['acl-Override'] = SharingPermissions.Unset;
             dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
-            dataProps.system = viewProps.system;
-            dataProps.isPrototype = true;
+            dataProps.isSystem = viewProps.isSystem;
+            dataProps.isDataDoc = true;
             dataProps.author = Doc.CurrentUserEmail;
             dataProps.creationDate = new DateField();
             if (fieldKey) {
-                dataProps[`${fieldKey}-lastModified`] = new DateField();
+                dataProps[`${fieldKey}_modificationDate`] = new DateField();
                 dataProps[fieldKey] = data;
 
                 // so that the list of annotations is already initialised, prevents issues in addonly.
                 // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
-                dataProps[fieldKey + '-annotations'] = new List<Doc>();
-                dataProps[fieldKey + '-sidebar'] = new List<Doc>();
+                dataProps[fieldKey + '_annotations'] = new List<Doc>();
+                dataProps[fieldKey + '_sidebar'] = new List<Doc>();
             }
 
             // users placeholderDoc as proto if it exists
@@ -901,7 +902,7 @@ export namespace Docs {
         }
 
         export function ComparisonDocument(options: DocumentOptions = { title: 'Comparison Box' }) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options);
+            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), undefined, options);
         }
 
         export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
@@ -954,11 +955,12 @@ export namespace Docs {
                 Prototypes.get(DocumentType.LINK),
                 undefined,
                 {
-                    anchor1: source,
-                    anchor2: target,
+                    link_anchor_1: source,
+                    link_anchor_2: target,
                     ...options,
                 },
-                id
+                id,
+                'link'
             );
 
             LinkManager.Instance.addLink(linkDoc);
@@ -966,36 +968,24 @@ export namespace Docs {
             return linkDoc;
         }
 
-        export function InkDocument(
-            color: string,
-            tool: string,
-            strokeWidth: number,
-            strokeBezier: string,
-            fillColor: string,
-            arrowStart: string,
-            arrowEnd: string,
-            dash: string,
-            points: PointData[],
-            isInkMask: boolean,
-            options: DocumentOptions = {}
-        ) {
+        export function InkDocument(color: string, strokeWidth: number, stroke_bezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], isInkMask: boolean, options: DocumentOptions = {}) {
             const I = new Doc();
             I[Initializing] = true;
-            I.isInkMask = isInkMask;
             I.type = DocumentType.INK;
-            I.layout = InkingStroke.LayoutString('data');
-            I.color = color;
-            I.hideDecorationTitle = true; // don't show title when selected
+            I.layout = InkingStroke.LayoutString('stroke');
+            I.layout_fitWidth = true;
+            I.layout_hideDecorationTitle = true; // don't show title when selected
             // I.hideOpenButton = true; // don't show open full screen button when selected
+            I.color = color;
             I.fillColor = fillColor;
-            I.strokeWidth = strokeWidth;
-            I.strokeBezier = strokeBezier;
-            I.strokeStartMarker = arrowStart;
-            I.strokeEndMarker = arrowEnd;
-            I.strokeDash = dash;
-            I.tool = tool;
-            I.fitWidth = true;
-            I['text-align'] = 'center';
+            I.stroke = new InkField(points);
+            I.stroke_width = strokeWidth;
+            I.stroke_bezier = stroke_bezier;
+            I.stroke_startMarker = arrowStart;
+            I.stroke_endMarker = arrowEnd;
+            I.stroke_dash = dash;
+            I.stroke_isInkMask = isInkMask;
+            I.text_align = 'center';
             I.title = 'ink';
             I.x = options.x as number;
             I.y = options.y as number;
@@ -1004,7 +994,6 @@ export namespace Docs {
             I._fontFamily = 'cursive';
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
-            I.data = new InkField(points);
             I.defaultDoubleClick = 'click';
             I.creationDate = new DateField();
             I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
@@ -1050,7 +1039,7 @@ export namespace Docs {
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id);
-            documents.map(d => (d.context = inst));
+            documents.map(d => (d.embedContainer = inst));
             return inst;
         }
 
@@ -1136,7 +1125,7 @@ export namespace Docs {
         }
 
         export function EquationDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.EQUATION), undefined, { ...(options || {}) });
+            return InstanceFromProto(Prototypes.get(DocumentType.EQUATION), undefined, { ...(options || {}) }, undefined, 'text');
         }
 
         export function FunctionPlotDocument(documents: Array<Doc>, options?: DocumentOptions) {
@@ -1263,7 +1252,7 @@ export namespace DocUtils {
                           ? true
                           : matches.some(value => {
                                 if (facetKey.startsWith('*')) {
-                                    //  fields starting with a '*' are used to match families of related fields.  ie, *lastModified will match text-lastModified, data-lastModified, etc
+                                    //  fields starting with a '*' are used to match families of related fields.  ie, *modificationDate will match text_modificationDate, data_modificationDate, etc
                                     const allKeys = Array.from(Object.keys(d));
                                     allKeys.push(...Object.keys(Doc.GetProto(d)));
                                     const keys = allKeys.filter(key => key.includes(facetKey.substring(1)));
@@ -1304,12 +1293,12 @@ export namespace DocUtils {
         broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
         return DocUtils.ActiveRecordings.map(audio => {
             const sourceDoc = getSourceDoc();
-            return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { linkDisplay: false, linkRelationship: 'recording annotation:linked recording', description: 'recording timeline' });
+            return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { layout_linkDisplay: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' });
         });
     }
 
-    export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, showPopup?: number[]) {
-        if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
+    export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; layout_linkDisplay?: boolean }, id?: string, showPopup?: number[]) {
+        if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
         const sv = DocumentManager.Instance.getDocumentView(source);
         if (target.doc === Doc.UserDoc()) return undefined;
 
@@ -1350,16 +1339,16 @@ export namespace DocUtils {
                 target,
                 {
                     title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
-                    'anchor1-useLinkSmallAnchor': source.useLinkSmallAnchor ? true : undefined,
-                    'anchor2-useLinkSmallAnchor': target.useLinkSmallAnchor ? true : undefined,
+                    link_anchor_1_useLinkSmallAnchor: source.useLinkSmallAnchor ? true : undefined,
+                    link_anchor_2_useLinkSmallAnchor: target.useLinkSmallAnchor ? true : undefined,
                     'acl-Public': SharingPermissions.Augment,
                     '_acl-Public': SharingPermissions.Augment,
-                    linkDisplay: linkSettings.linkDisplay,
-                    _linkAutoMove: true,
-                    linkRelationship: linkSettings.linkRelationship,
-                    _showCaption: 'description',
-                    _showTitle: 'linkRelationship',
-                    description: linkSettings.description,
+                    layout_linkDisplay: linkSettings.layout_linkDisplay,
+                    link_relationship: linkSettings.link_relationship,
+                    link_description: linkSettings.link_description,
+                    _layout_autoMoveAnchors: true,
+                    _layout_showCaption: 'link_description',
+                    _layout_showTitle: 'link_relationship',
                 },
                 id
             ),
@@ -1443,13 +1432,13 @@ export namespace DocUtils {
             created = Docs.Create.AudioDocument(field.url.href, resolved);
             created.layout = AudioBox.LayoutString(fieldKey);
         } else if (field instanceof InkField) {
-            created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved);
             created.layout = InkingStroke.LayoutString(fieldKey);
         } else if (field instanceof List && field[0] instanceof Doc) {
             created = Docs.Create.StackingDocument(DocListCast(field), resolved);
             created.layout = CollectionView.LayoutString(fieldKey);
         } else {
-            created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
+            created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved });
             created.layout = FormattedTextBox.LayoutString(fieldKey);
         }
         if (created) {
@@ -1503,12 +1492,12 @@ export namespace DocUtils {
                 const id = s[s.length - 1];
                 return DocServer.GetRefField(id).then(field => {
                     if (field instanceof Doc) {
-                        const alias = Doc.MakeAlias(field);
-                        alias.x = (options.x as number) || 0;
-                        alias.y = (options.y as number) || 0;
-                        alias._width = (options._width as number) || 300;
-                        alias._height = (options._height as number) || (options._width as number) || 300;
-                        return alias;
+                        const embedding = Doc.MakeEmbedding(field);
+                        embedding.x = (options.x as number) || 0;
+                        embedding.y = (options.y as number) || 0;
+                        embedding._width = (options._width as number) || 300;
+                        embedding._height = (options._height as number) || (options._width as number) || 300;
+                        return embedding;
                     }
                     return undefined;
                 });
@@ -1531,11 +1520,11 @@ export namespace DocUtils {
                             _width: 200,
                             x,
                             y,
-                            _autoHeight: note._autoHeight !== false,
-                            title: StrCast(note.title) + '#' + (note.aliasCount = NumCast(note.aliasCount) + 1),
+                            _layout_autoHeight: note._layout_autoHeight !== false,
+                            title: StrCast(note.title) + '#' + (note.embeddingCount = NumCast(note.embeddingCount) + 1),
                         });
-                        textDoc.layoutKey = 'layout_' + note.title;
-                        textDoc[textDoc.layoutKey] = note;
+                        textDoc.layout_fieldKey = 'layout_' + note.title;
+                        textDoc[textDoc.layout_fieldKey] = note;
                         if (pivotField) {
                             textDoc[pivotField] = pivotValue;
                         }
@@ -1576,7 +1565,7 @@ export namespace DocUtils {
     export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
         const batch = UndoManager.StartBatch('makeCustomViewClicked');
         runInAction(() => {
-            doc.layoutKey = 'layout_' + templateSignature;
+            doc.layout_fieldKey = 'layout_' + templateSignature;
             createCustomView(doc, creator, templateSignature, docLayoutTemplate);
         });
         batch.end();
@@ -1607,7 +1596,7 @@ export namespace DocUtils {
         const customName = 'layout_' + templateSignature;
         const _width = NumCast(doc._width);
         const _height = NumCast(doc._height);
-        const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _showSidebar: false };
+        const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _layout_autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _layout_showSidebar: false };
 
         if (docLayoutTemplate) {
             if (docLayoutTemplate !== doc[customName]) {
@@ -1638,9 +1627,9 @@ export namespace DocUtils {
         }
     }
     export function iconify(doc: Doc) {
-        const layoutKey = Cast(doc.layoutKey, 'string', null);
+        const layout_fieldKey = Cast(doc.layout_fieldKey, 'string', null);
         DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined);
-        if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') doc.deiconifyLayout = layoutKey.replace('layout_', '');
+        if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') doc.deiconifyLayout = layout_fieldKey.replace('layout_', '');
     }
 
     export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
@@ -1680,8 +1669,8 @@ export namespace DocUtils {
 
     export function LeavePushpin(doc: Doc, annotationField: string) {
         if (doc.followLinkToggle) return undefined;
-        const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null);
-        const hasContextAnchor = LinkManager.Links(doc).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context));
+        const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null);
+        const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context));
         if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
             const pushpin = Docs.Create.FontIconDocument({
                 title: 'pushpin',
@@ -1700,7 +1689,7 @@ export namespace DocUtils {
                 _timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
             });
             Doc.AddDocToList(context, annotationField, pushpin);
-            const pushpinLink = DocUtils.MakeLink(pushpin, doc, { linkRelationship: 'pushpin' }, '');
+            const pushpinLink = DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, '');
             doc._timecodeToShow = undefined;
             return pushpin;
         }
@@ -1745,21 +1734,17 @@ export namespace DocUtils {
         if (doc) {
             const proto = Doc.GetProto(doc);
             proto.text = result.rawText;
-            proto.fileUpload = pathname
-                .replace(/.*\//, '')
-                .replace('upload_', '')
-                .replace(/\.[a-z0-9]*$/, '');
             if (Upload.isImageInformation(result)) {
                 const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
                 const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
                 proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined);
-                proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
-                proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
                 if (NumCast(proto['data-nativeOrientation']) >= 5) {
-                    proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
-                    proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                    proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                    proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
                 }
-                proto.contentSize = result.contentSize;
+                proto.data_contentSize = result.contentSize;
                 // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
                 const latitude = result.exifData?.data?.GPSLatitude;
                 const latitudeDirection = result.exifData?.data?.GPSLatitudeRef;
@@ -1771,12 +1756,12 @@ export namespace DocUtils {
                 }
             }
             if (Upload.isVideoInformation(result)) {
-                proto['data-duration'] = result.duration;
+                proto.data_duration = result.duration;
             }
             if (overwriteDoc) {
                 Doc.removeCurrentlyLoading(overwriteDoc);
                 // loading doc icons are just labels. so any icon views of loading docs need to be replaced with the proper icon view.
-                DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layoutKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
+                DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
             }
             generatedDocuments.push(doc);
         }
@@ -1793,16 +1778,16 @@ export namespace DocUtils {
             _height: 35,
             x: x,
             y: y,
-            _fitWidth: true,
-            _autoHeight: true,
-            _showAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
+            _layout_fitWidth: true,
+            _layout_autoHeight: true,
+            _layout_altContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
             title,
         });
         const template = Doc.UserDoc().defaultTextLayout;
         if (template instanceof Doc) {
             tbox._width = NumCast(template._width);
-            tbox.layoutKey = 'layout_' + StrCast(template.title);
-            Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
+            tbox.layout_fieldKey = 'layout_' + StrCast(template.title);
+            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template;
         }
         return tbox;
     }
@@ -1857,9 +1842,9 @@ export namespace DocUtils {
         if (!dragFactory) return undefined;
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
         ndoc && Doc.AddFileOrphan(Doc.GetProto(ndoc));
-        if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
-            dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
-            Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(), true);
+        if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
+            dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
+            Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
         }
 
         if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc);
@@ -1868,9 +1853,9 @@ export namespace DocUtils {
     }
     export function delegateDragFactory(dragFactory: Doc) {
         const ndoc = Doc.MakeDelegateWithProto(dragFactory);
-        if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
-            dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
-            Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString();
+        if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
+            dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
+            Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString();
         }
         return ndoc;
     }
@@ -1885,11 +1870,8 @@ ScriptingGlobals.add(function makeDelegate(proto: any) {
     return d;
 });
 ScriptingGlobals.add(function generateLinkTitle(self: Doc) {
-    const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null)?.title : '<?>';
-    const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null)?.title : '<?>';
-    const relation = self.linkRelationship || 'to';
-    return `${anchor1title} (${relation}) ${anchor2title}`;
-});
-ScriptingGlobals.add(function openTabAlias(tab: Doc) {
-    CollectionDockingView.AddSplit(Doc.MakeAlias(tab), OpenWhereMod.right);
+    const link_anchor_1title = self.link_anchor_1 && self.link_anchor_1 !== self ? Cast(self.link_anchor_1, Doc, null)?.title : '<?>';
+    const link_anchor_2title = self.link_anchor_2 && self.link_anchor_2 !== self ? Cast(self.link_anchor_2, Doc, null)?.title : '<?>';
+    const relation = self.link_relationship || 'to';
+    return `${link_anchor_1title} (${relation}) ${link_anchor_2title}`;
 });
diff --git a/src/client/documents/Gitlike.ts b/src/client/documents/Gitlike.ts
index 5e2baf924..f35f33f65 100644
--- a/src/client/documents/Gitlike.ts
+++ b/src/client/documents/Gitlike.ts
@@ -9,24 +9,24 @@
 // // we don't have individual timestamps for all fields -- this is a problematic design issue.
 // function GitlikeSynchDocs(bd: Doc, md: Doc) {
 //     const fieldKey = Doc.LayoutFieldKey(md);
-//     const bdate = DateCast(bd[`${fieldKey}-lastModified`])?.date;
-//     const mdate = DateCast(md[`${fieldKey}-lastModified`])?.date;
+//     const bdate = DateCast(bd[`${fieldKey}_modificationDate`])?.date;
+//     const mdate = DateCast(md[`${fieldKey}_modificationDate`])?.date;
 //     const bdproto = bd && Doc.GetProto(bd);
 //     if (bdate !== mdate && bdate <= mdate) {
 //         if (bdproto && md) {
 //             bdproto[fieldKey] = Field.Copy(md[fieldKey]);
-//             bdproto[`${fieldKey}-lastModified`] = new DateField();
+//             bdproto[`${fieldKey}_modificationDate`] = new DateField();
 //         }
 //     }
-//     const bldate = DateCast(bd._lastModified)?.date;
-//     const mldate = DateCast(md._lastModified)?.date;
+//     const bldate = DateCast(bd._layout_modificationDate)?.date;
+//     const mldate = DateCast(md._layout_modificationDate)?.date;
 //     if (bldate === mldate || bldate > mldate) return;
 //     if (bdproto && md) {
 //         bd.x = Field.Copy(md.x);
 //         bd.y = Field.Copy(md.y);
 //         bd.width = Field.Copy(md.width);
 //         bd.height = Field.Copy(md.height);
-//         bdproto._lastModified = new DateField();
+//         bdproto._layout_modificationDate = new DateField();
 //     }
 // }
 
@@ -53,10 +53,10 @@
 //     const newlyBranchedDocs = await Promise.all(newDocsFromMaster?.map(async md => (await Doc.MakeClone(md, false, true, cloneMap)).clone) || []);
 //     newlyBranchedDocs.forEach(nd => {
 //         Doc.AddDocToList(branch, Doc.LayoutFieldKey(branch) + suffix, nd);
-//         nd.context = branch;
+//         nd.embedContainer = branch;
 //     });
-//     // if a branch doc's corresponding main branch doc doesn't have a context, then it was deleted.
-//     const remDocsFromMaster = branchMainDocs?.filter(bd => Cast(bd.branchOf, Doc, null) && !Cast(bd.branchOf, Doc, null)?.context);
+//     // if a branch doc's corresponding main branch doc doesn't have a embedContainer, then it was deleted.
+//     const remDocsFromMaster = branchMainDocs?.filter(bd => Cast(bd.branchOf, Doc, null) && !Cast(bd.branchOf, Doc, null)?.embedContainer);
 //     // so then remove all the deleted main docs from this branch.
 //     remDocsFromMaster?.forEach(rd => Doc.RemoveDocFromList(branch, Doc.LayoutFieldKey(branch) + suffix, rd));
 // }
@@ -87,14 +87,14 @@
 //                 branchDocProto.branchOf = masterChild;                                 // the branch child is now a branch of the master child
 //             }
 //             Doc.AddDocToList(master, Doc.LayoutFieldKey(master) + suffix, masterChild); // add the masterChild to master (if it's already there, this is a no-op)
-//             masterChild.context = master;
+//             masterChild.embedContainer = master;
 //             GitlikeSynchDocs(masterChild, bd);//Doc.GetProto(masterChild), bd);
 //         }));
 //         const masterChildren = await DocListCastAsync(master[Doc.LayoutFieldKey(master) + suffix]);
 //         masterChildren?.forEach(mc => {                      // see if any master children
 //             if (!branchChildren?.find(bc => bc.branchOf === mc)) { //    are not in the list of children for this branch.
 //                 Doc.RemoveDocFromList(master, Doc.LayoutFieldKey(master) + suffix, mc); // if so, delete the master child since the branch has deleted it.
-//                 mc.context = undefined;      // NOTE if we merge a branch that didn't do a pull, it will look like the branch deleted documents -- need edit timestamps that prevent merging if branch isn't up-to-date with last edit timestamp
+//                 mc.embedContainer = undefined;      // NOTE if we merge a branch that didn't do a pull, it will look like the branch deleted documents -- need edit timestamps that prevent merging if branch isn't up-to-date with last edit timestamp
 //             }
 //         });
 //     });
@@ -109,8 +109,8 @@
 // export async function BranchTask(target: Doc, action: "pull" | "merge") {
 //     const func = action === "pull" ? GitlikePullFromMaster : GitlikeMergeWithMaster;
 //     await func(target, "");
-//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "-annotations"));
-//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "-sidebar"));
+//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "_annotations"));
+//     await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "_sidebar"));
 // }
 
 // export async function BranchCreate(target: Doc) {
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index c9fcc84a3..d68761ba7 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -53,7 +53,7 @@ export class CaptureManager extends React.Component<{}> {
                 order.push(
                     <div className="list-item">
                         <div className="number">{i}</div>
-                        {StrCast(DocCast(l.anchor1)?.title)}
+                        {StrCast(DocCast(l.link_anchor_1)?.title)}
                     </div>
                 )
             );
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index fbfe306a4..5cfd77937 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -20,7 +20,7 @@ import { TreeViewType } from "../views/collections/CollectionTreeView";
 import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
-import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
+import { ButtonType } from "../views/nodes/button/FontIconBox";
 import { OpenWhere } from "../views/nodes/DocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DragManager } from "./DragManager";
@@ -37,7 +37,6 @@ interface Button {
     toolTip?: string;
     icon?: string;
     btnType?: ButtonType;
-    numBtnType?: NumButtonType;
     numBtnMin?: number;
     numBtnMax?: number;
     switchToggle?: boolean;
@@ -65,11 +64,11 @@ export class CurrentUserUtils {
         const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [
             {
                 btnOpts: { title: "slide", icon: "address-card" }, 
-                templateOpts: { _width: 400, _height: 300, title: "slideView",  _xMargin: 3, _yMargin: 3, system: true }, 
+                templateOpts: { _width: 400, _height: 300, title: "slideView",  _xMargin: 3, _yMargin: 3, isSystem: true }, 
                 template: (opts:DocumentOptions) => Docs.Create.MultirowDocument(
                     [
-                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200,  system: true }),
-                        Docs.Create.TextDocument("", { title: "text", _fitWidth:true,  _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
+                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200,  isSystem: true }),
+                        Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true,  _height: 100, isSystem: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
                     ], opts)
             },
             {
@@ -97,9 +96,9 @@ export class CurrentUserUtils {
         });
 
         const reqdOpts:DocumentOptions = {
-            title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
-            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
-            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
+            title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true,
+            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, isSystem: true,
+            _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
         const reqdFuncs = { hidden: "IsNoviceMode()" };
@@ -109,7 +108,7 @@ export class CurrentUserUtils {
     /// Initializes templates for editing click funcs of a document 
     static setupChildClickEditors(doc: Doc, field = "clickFuncs-child") {
         const tempClicks = DocCast(doc[field]);
-        const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, system: true};
+        const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
             { opts: { title: "Open In Target",        targetScriptKey: "onChildClick"},       script: "docCast(documentView?.props.docViewPath().lastElement()?.rootDoc.target).then((target) => target && (target.proto.data = new List([self])))"},
             { opts: { title: "Open Detail On Right",  targetScriptKey: "onChildDoubleClick"}, script: `openDoc(self.doubleClickView.${OpenWhere.addRight})`}];
@@ -119,14 +118,14 @@ export class CurrentUserUtils {
             return DocUtils.AssignOpts(clickDoc, allOpts) ?? Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, allOpts),allOpts);
         });
 
-        const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, system: true};
+        const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, isSystem: true};
         return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
     }
 
     /// Initializes templates for editing click funcs of a document 
     static setupClickEditorTemplates(doc: Doc, field = "template-clickFuncs") {
         const tempClicks = DocCast(doc[field]);
-        const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, system: true};
+        const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
             { opts: { title: "onClick"},             script: "console.log('click')"},
             { opts: { title: "onDoubleClick"},       script: "console.log('click')"},
@@ -140,7 +139,7 @@ export class CurrentUserUtils {
             return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title);
         });
 
-        const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, system: true};
+        const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true};
         return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
     }
 
@@ -152,12 +151,12 @@ export class CurrentUserUtils {
             { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
             { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
         const reqdNoteList = reqdTempOpts.map(opts => {
-            const reqdOpts = {...opts, title: "text", width:200, autoHeight: true, fitWidth: true};
+            const reqdOpts = {...opts, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
             const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
             return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
         });
 
-        const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true };
+        const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
         return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
     }
 
@@ -169,14 +168,14 @@ export class CurrentUserUtils {
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
         CurrentUserUtils.setupChildClickEditors(doc)
-        const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
+        const reqdOpts = { title: "template layouts", _xMargin: 0, isSystem: true,  };
         const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
         return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
     static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
-        const reqdOpts = { title: "icon templates", _height: 75, system: true };
+        const reqdOpts = { title: "icon templates", _height: 75, isSystem: true };
         const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
@@ -187,7 +186,7 @@ export class CurrentUserUtils {
                 case DocumentType.IMG : creator = imageBox; break;
                 case DocumentType.FONTICON: creator = fontBox; break;
             }
-            const allopts = {system: true, onClickScriptDisable: "never",  ...opts};
+            const allopts = {isSystem: true, onClickScriptDisable: "never",  ...opts};
             return DocUtils.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
                       DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
                 {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
@@ -195,14 +194,14 @@ export class CurrentUserUtils {
         const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
             textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
-        const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _showTitle: "title", ...opts });
+        const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _layout_showTitle: "title", ...opts });
         const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
         const iconTemplates = [
             makeIconTemplate(undefined,          "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}),
             makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}),
             makeIconTemplate(DocumentType.PDF,   "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}),
             makeIconTemplate(DocumentType.WEB,   "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "brown"}),
-            makeIconTemplate(DocumentType.RTF,   "text",  { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}),
+            makeIconTemplate(DocumentType.RTF,   "text",  { iconTemplate:DocumentType.LABEL, _layout_showTitle: "creationDate"}),
             makeIconTemplate(DocumentType.IMG,   "data",  { iconTemplate:DocumentType.IMG,   _height: undefined}),
             makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
             makeIconTemplate(DocumentType.COL,   "icon",  { iconTemplate:DocumentType.IMG}),
@@ -220,7 +219,7 @@ export class CurrentUserUtils {
         title: string, toolTip: string, icon: string,  ignoreClick?: boolean, dragFactory?: Doc, 
         backgroundColor?: string, openFactoryAsDelegate?:boolean, openFactoryLocation?:string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string},
     }[] {
-        const standardOps = (key:string) => ({ title : "Untitled "+ key, _fitWidth: false, system: true, "dragFactory-count": 0, cloneFieldFilter: new List<string>(["system"]) });
+        const standardOps = (key:string) => ({ title : "Untitled "+ key, _layout_fitWidth: false, isSystem: true, "dragFactory_count": 0, cloneFieldFilter: new List<string>(["isSystem"]) });
         const json = {
             doc: {
                 type: "doc",
@@ -247,7 +246,7 @@ export class CurrentUserUtils {
                 "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
                 `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
                 "    <FormattedTextBox {...props} dontScale='true' fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize||9}px' height='{(this._headerHeight||0)}px' backgroundColor='{this._headerColor || MySharedDocs().userColor||`lightGray`}' />" +
-                `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' backgroundColor='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) + (this._autoHeightMargins=this._headerHeight ? this._headerHeight+${headerBtnHgt}:0)’}  >Metadata</HTMLdiv>` +
+                `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' backgroundColor='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) + (this._layout_autoHeightMargins=this._headerHeight ? this._headerHeight+${headerBtnHgt}:0)’}  >Metadata</HTMLdiv>` +
                 "</HTMLdiv>"
             }, "header");
 
@@ -263,27 +262,27 @@ export class CurrentUserUtils {
                           funcs?:{[key:string]: any},  // computed fields that are rquired for the empth thing
                           creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
               }[] = [
-            {key: "Note",       creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _autoHeight: true }},
-            {key: "Flashcard",  creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _autoHeight: true, _showAltContentUI: true}},
-            {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, _backgroundGridShow: true, }},
-            {key: "Noteboard",  creator: opts => Docs.Create.NoteTakingDocument([], opts),  opts: { _width: 250, _height: 200, _fitWidth: true}},
-            {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 150, _height: 100, _fitWidth: true }},
-            {key: "Webpage",    creator: opts => Docs.Create.WebDocument("",opts),          opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }},
+            {key: "Note",       creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _layout_autoHeight: true }},
+            {key: "Flashcard",  creator: opts => Docs.Create.TextDocument("", opts),        opts: { _width: 200, _layout_autoHeight: true, _layout_altContentUI: true}},
+            {key: "Equation",   creator: opts => Docs.Create.EquationDocument(opts),        opts: { _width: 300, _height: 35, }},
+            {key: "Noteboard",  creator: opts => Docs.Create.NoteTakingDocument([], opts),  opts: { _width: 250, _height: 200, _layout_fitWidth: true}},
+            {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 150, _height: 100, _layout_fitWidth: true }},
+            {key: "Webpage",    creator: opts => Docs.Create.WebDocument("",opts),          opts: { _width: 400, _height: 512, _nativeWidth: 850, data_useCors: true, }},
             {key: "Comparison", creator: Docs.Create.ComparisonDocument,                    opts: { _width: 300, _height: 300 }},
             {key: "Audio",      creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }},
-            {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _fitWidth: true, _showSidebar: true,  }},
+            {key: "Map",        creator: opts => Docs.Create.MapDocument([], opts),         opts: { _width: 800, _height: 600, _layout_fitWidth: true, _layout_showSidebar: true,  }},
             {key: "Screengrab", creator: Docs.Create.ScreenshotDocument,                    opts: { _width: 400, _height: 200 }},
-            {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
+            {key: "WebCam",     creator: opts => Docs.Create.WebCamDocument("", opts),      opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
             {key: "Button",     creator: Docs.Create.ButtonDocument,                        opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: FollowLinkScript()}},
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
-            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, treeViewHideUnrendered: true}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, treeViewHideTitle: true, _fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }},
-            {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _fitWidth: true, _backgroundGridShow: true, }},
+            {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeViewHideUnrendered: true}},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "embed" as any, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }},
+            {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, 
-                                                                                                treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, 
+                                                                                                treeViewHasOverlay: true, _fontSize: "20px", _layout_autoHeight: true, 
                                                                                                 allowOverlayDrop: true, treeViewType: TreeViewType.outline, 
-                                                                                                backgroundColor: "white", _xMargin: 0, _yMargin: 0, _singleLine: true 
+                                                                                                backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
 
@@ -320,16 +319,16 @@ export class CurrentUserUtils {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
             const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, 
                 _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, 
-                btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
+                btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
         });
 
         const reqdOpts:DocumentOptions = {
-            title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true,
-            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-             childDropAction: 'alias' 
+            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, isSystem: true,
+            _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
+             childDropAction: 'embed' 
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -354,7 +353,7 @@ export class CurrentUserUtils {
 
     /// the empty panel that is filled with whichever left menu button's panel has been selected
     static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
-        DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
+        DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.isSystem = true; return doc;})(new Doc()), {isSystem:true});
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
@@ -364,8 +363,8 @@ export class CurrentUserUtils {
         const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
-                title, icon, target, btnType: ButtonType.MenuButton, system: true, dontUndo: true,  dontRegisterView: true,
-                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+                title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true,  dontRegisterView: true,
+                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
             };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
@@ -373,14 +372,14 @@ export class CurrentUserUtils {
 
         const reqdStackOpts:DocumentOptions ={
             title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
-            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true,
+            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
     }
 
     // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
     static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") {
-        const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,};
+        const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, isSystem: true, _chromeHidden: true,};
         DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
     }
 
@@ -401,9 +400,9 @@ export class CurrentUserUtils {
                 title: data.title,
                 _lockedPosition: true,
                 onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
-                backgroundColor: data.backgroundColor, system: true
+                backgroundColor: data.backgroundColor, isSystem: true
             },
-                [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
+                [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", isSystem: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
         );
     }
 
@@ -411,51 +410,51 @@ export class CurrentUserUtils {
     static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, {
         ...opts,
          _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15,
-        borderRounding: "5px", boxShadow: "0 0", system: true
+        borderRounding: "5px", boxShadow: "0 0", isSystem: true
     }) as any as Doc
 
     // sets up the text container for the information contained within the mobile button
     static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, {
         ...opts,
         _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25,
-        backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, system: true
+        backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, isSystem: true
     }) as any as Doc
 
     // Sets up the title of the button
     static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, {
         ...opts,
-        title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", system: true
+        title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", isSystem: true
     }) as any as Doc
 
     // Sets up the description of the button
     static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, {
         ...opts,
-        title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true
+        title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, isSystem: true
     }) as any as Doc
 
 
 
     static setupMobileInkingDoc(userDoc: Doc) {
-        return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true });
+        return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", isSystem: true });
     }
 
     static setupMobileUploadDoc(userDoc: Doc) {
         // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" })
         const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", {
-            title: "Upload Images From the Web", _lockedPosition: true, system: true
+            title: "Upload Images From the Web", _lockedPosition: true, isSystem: true
         });
         const uploadDoc = Docs.Create.StackingDocument([], {
-            title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, system: true, _chromeHidden: true,
+            title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, isSystem: true, _chromeHidden: true,
         });
         return Docs.Create.StackingDocument([webDoc, uploadDoc], {
-            _width: screen.width, _lockedPosition: true, title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", system: true, _chromeHidden: true,
+            _width: screen.width, _lockedPosition: true, title: "Upload", _layout_autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", isSystem: true, _chromeHidden: true,
         });
     }
 
     /// Search option on the left side button panel
     static setupSearcher(doc: Doc, field:string) {
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", 
+                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDropAction: "embed", 
                 _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true,  });
     }
 
@@ -465,8 +464,8 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
         const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
-            title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
-            _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+            title: "My Tools", isSystem: true, ignoreClick: true, boxShadow: "0 0",
+            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
     }
@@ -479,7 +478,7 @@ export class CurrentUserUtils {
         const newDashboard = `createNewDashboard()`;
 
         const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
-             title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
+             title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true };
         const reqdBtnScript = {onClick: newDashboard,}
         const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
        
@@ -492,9 +491,9 @@ export class CurrentUserUtils {
         const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true,
-            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
-            buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "alias",
-            _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
+            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
+            buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "embed",
+            _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
             childContextMenuLabels:new List<string>(childContextMenuLabels),
@@ -517,20 +516,20 @@ export class CurrentUserUtils {
     /// initializes the left sidebar File system pane
     static setupFilesystem(doc: Doc, field:string) {
         var myFilesystem = DocCast(doc[field]);
-        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, isSystem: true, isFolder: true });
         
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
             _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30,
-            title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true
+            title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: newFolder};
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
-        const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
-            title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true,
+        const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
+            title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true,
             isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, 
-            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "alias",
+            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed",
             childContextMenuLabels: new List<string>(["Create new folder"]),
             childContextMenuIcons: new List<string>(["plus"]),
             explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
@@ -545,8 +544,8 @@ export class CurrentUserUtils {
 
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
-        const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
-            title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true,
+        const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
+            title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true,
             treeViewTruncateTitleWidth: 350, ignoreClick: true,  boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
@@ -556,7 +555,7 @@ export class CurrentUserUtils {
 
         const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
         const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, 
-            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", system: true,
+            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", isSystem: true,
             toolTip: "Empty recently closed",};
         const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
 
@@ -572,7 +571,7 @@ export class CurrentUserUtils {
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
-            boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
+            boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true,
             treeViewHideTitle: true, treeViewTruncateTitleWidth: 350
         };
         if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
@@ -582,13 +581,13 @@ export class CurrentUserUtils {
     static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
         ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-        _lockedPosition: true, system: true, flexDirection: "row"
+        _lockedPosition: true, isSystem: true, flexDirection: "row"
     })
 
     static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
         btnType: ButtonType.ToolButton, _forceActive: true, _hideContextMenu: true, 
         _removeDropProperties: new List<string>([ "_hideContextMenu", "stayInCollection"]), 
-        _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts,
+        _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts,
     })
 
     /// initializes the required buttons in the expanding button menu at the bottom of the Dash window
@@ -606,7 +605,7 @@ export class CurrentUserUtils {
         ];
         const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
-            title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'alias',
+            title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'embed',
             childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
         };
         reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
@@ -623,7 +622,7 @@ export class CurrentUserUtils {
     }
     static viewTools(): Button[] {
         return [
-            { title: "Snap\xA0Lines", icon: "th",           toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Snap\xA0Lines", icon: "th",           toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snaplines",  funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Grid",          icon: "border-all",   toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "View\xA0All",   icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Clusters",      icon: "braille",      toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
@@ -635,7 +634,7 @@ export class CurrentUserUtils {
         return [
             { title: "Font",    toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
-            { title: "Size",    toolTip: "Font size (%size)",    btnType: ButtonType.NumberButton, width: 75,           toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions },
+            { title: "Size",    toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton, width: 75,           toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 },
             { title: "Color",   toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}},
             { title: "Highlight",toolTip:"Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
             { title: "Bold",    toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
@@ -663,7 +662,7 @@ export class CurrentUserUtils {
             { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square",     toolType:GestureUtils.Gestures.Rectangle,   scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} },
             { title: "Line",   toolTip: "Line (double tap to lock mode)",   btnType: ButtonType.ToggleButton, icon: "minus",      toolType:GestureUtils.Gestures.Line,        scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} },
             { title: "Mask",   toolTip: "Mask",                             btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask",                        scripts: {onClick:'{ return setInkProperty(self.toolType, value, _readOnly_);}'} },
-            { title: "Width",  toolTip: "Stroke width",                     btnType: ButtonType.NumberButton,                     toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1},
+            { title: "Width",  toolTip: "Stroke width",                     btnType: ButtonType.NumberSliderButton,               toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnMin: 1},
             { title: "Ink",    toolTip: "Stroke color",                     btnType: ButtonType.ColorButton,  icon: "pen",        toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'} },
         ];
     }
@@ -712,7 +711,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
             backgroundColor: params.backgroundColor ??"transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
-            color: Colors.WHITE, system: true, dontUndo: true, 
+            color: Colors.WHITE, isSystem: true, dontUndo: true, 
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
             toolType: params.toolType, expertMode: params.expertMode,
@@ -728,7 +727,7 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'alias', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
@@ -750,11 +749,11 @@ export class CurrentUserUtils {
 
     /// collection of documents rendered in the overlay layer above all tabs and other UI
     static setupOverlays(doc: Doc, field = "myOverlayDocs") {
-        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", isSystem: true });
     }
 
     static setupPublished(doc:Doc, field = "myPublishedDocs") {
-        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", isSystem: true });
     }
     
     /// The database of all links on all documents
@@ -788,9 +787,9 @@ export class CurrentUserUtils {
             childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
-            childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
-            // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar
-            _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
+            childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
+            // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
+            _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
             explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
         };
         
@@ -800,16 +799,16 @@ export class CurrentUserUtils {
     /// Import option on the left side button panel 
     static setupImportSidebar(doc: Doc, field:string) {   
         const reqdOpts:DocumentOptions = {
-            title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _showTitle: "title", 
+            title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _layout_showTitle: "title", 
             _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
-            childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
+            childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
             dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
         };
         const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
     
         const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
             _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
-            buttonText: "Import", icon: "upload", system: true };
+            buttonText: "Import", icon: "upload", isSystem: true };
         DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
         return myImports;
     }
@@ -818,13 +817,13 @@ export class CurrentUserUtils {
     /// whether to revert to "default" values, or to leave them as the user/system last set them.
     static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
-        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
+        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data_modificationDate"]),
             async () => {
                 const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
                 SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
-        doc.system ?? (doc.system = true);
+        doc.isSystem ?? (doc.isSystem = true);
         doc.title ?? (doc.title = Doc.CurrentUserEmail);
         Doc.noviceMode ?? (Doc.noviceMode = true);
         doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
@@ -860,7 +859,7 @@ export class CurrentUserUtils {
         this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true }); // drop down panel at top of dashboard for stashing documents
         
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
 
@@ -874,13 +873,13 @@ export class CurrentUserUtils {
         return doc;
     }
     static setupFieldInfos(doc:Doc, field="fieldInfos") {
-        const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
+        const fieldInfoOpts = { title: "Field Infos", isSystem: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
         const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
         const entries = Object.entries(new DocumentOptions());
         entries.forEach(pair => {
             if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) {
                 const options = pair[1] as FInfo;
-                const opts:DocumentOptions = { system: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")};
+                const opts:DocumentOptions = { isSystem: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")};
                 switch (options.fieldType) {
                     case "boolean": opts.fieldValues = new List<boolean>(options.values as any); break;
                     case "number": opts.fieldValues = new List<number>(options.values as any); break;
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index e116ae14b..1cce21034 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -327,7 +327,7 @@ export namespace DictationManager {
                 'new outline',
                 {
                     action: (target: DocumentView) => {
-                        const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _autoHeight: true });
+                        const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _layout_autoHeight: true });
                         const proto = newBox.proto!;
                         const prompt = 'Press alt + r to start dictating here...';
                         const head = 3;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 695b003a6..6eed4dc86 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -69,12 +69,12 @@ export class DocumentManager {
     public AddView = (view: DocumentView) => {
         //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
         if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const viewAnchorIndex = view.props.LayoutTemplateString.includes('anchor2') ? 'anchor2' : 'anchor1';
+            const viewAnchorIndex = view.props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
             const link = view.rootDoc;
             this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
                 this.LinkedDocumentViews.push({
-                    a: viewAnchorIndex === 'anchor2' ? otherView : view,
-                    b: viewAnchorIndex === 'anchor2' ? view : otherView,
+                    a: viewAnchorIndex === 'link_anchor_2' ? otherView : view,
+                    b: viewAnchorIndex === 'link_anchor_2' ? view : otherView,
                     l: link,
                 })
             );
@@ -173,7 +173,7 @@ export class DocumentManager {
 
         // heuristic to return the "best" documents first:
         //   choose a document in the lightbox first
-        //   choose an exact match over an alias match
+        //   choose an exact match over an embedding match
         lightViews.map(view => view.rootDoc === toFind && toReturn.push(view));
         lightViews.map(view => view.rootDoc !== toFind && Doc.AreProtosEqual(view.rootDoc, toFind) && toReturn.push(view));
         docViews.map(view => view.rootDoc === toFind && toReturn.push(view));
@@ -184,15 +184,15 @@ export class DocumentManager {
 
     static GetContextPath(doc: Opt<Doc>, includeExistingViews?: boolean) {
         if (!doc) return [];
-        const srcContext = DocCast(doc.annotationOn, DocCast(doc.context));
+        const srcContext = DocCast(doc.annotationOn, DocCast(doc.embedContainer));
         var containerDocContext = srcContext ? [srcContext, doc] : [doc];
         while (
             containerDocContext.length &&
-            containerDocContext[0]?.context &&
-            DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking &&
+            containerDocContext[0]?.embedContainer &&
+            DocCast(containerDocContext[0].embedContainer)?.viewType !== CollectionViewType.Docking &&
             (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0]))
         ) {
-            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+            containerDocContext = [Cast(containerDocContext[0].embedContainer, Doc, null), ...containerDocContext];
         }
         return containerDocContext;
     }
@@ -256,7 +256,7 @@ export class DocumentManager {
         if (options.openLocation === OpenWhere.lightbox) {
             // even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox)
             const target = DocCast(targetDoc.annotationOn, targetDoc);
-            const contextView = this.getDocumentView(DocCast(target.context));
+            const contextView = this.getDocumentView(DocCast(target.embedContainer));
             if (contextView?.docView?._componentView?.addDocTab?.(target, OpenWhere.lightbox)) {
                 await new Promise<void>(waitres => setTimeout(() => waitres()));
             }
@@ -275,7 +275,7 @@ export class DocumentManager {
     focusViewsInPath = async (docView: DocumentView, options: DocFocusOptions, iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt<Doc>; childDocView: Opt<DocumentView> }>) => {
         let contextView: DocumentView | undefined; // view containing context that contains target
         while (true) {
-            docView.rootDoc.layoutKey === 'layout_icon' ? await new Promise<void>(res => docView.iconify(res)) : undefined;
+            docView.rootDoc.layout_fieldKey === 'layout_icon' ? await new Promise<void>(res => docView.iconify(res)) : undefined;
             docView.props.focus(docView.rootDoc, options); // focus the view within its container
             const { childDocView, viewSpec } = await iterator(docView);
             if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView };
@@ -315,7 +315,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
         if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) {
             DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc));
         } else {
-            const container = DocCast(containingDoc ?? doc.context);
+            const container = DocCast(containingDoc ?? doc.embedContainer);
             const showDoc = !Doc.IsSystem(container) ? container : doc;
             options.toggleTarget = undefined;
             DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index b6de5604d..070e0f918 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,7 +15,7 @@ import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
 
-export type dropActionType = 'alias' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties
+export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties
 
 /**
  * Initialize drag
@@ -131,7 +131,7 @@ export namespace DragManager {
         canEmbed?: boolean;
         userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
         defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
-        dropAction: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'alias', but the document is dropped within the same collection, the drop action will be switched to 'move'
+        dropAction: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
         removeDropProperties?: string[];
         moveDocument?: MoveFunction;
         removeDocument?: RemoveFunction;
@@ -193,7 +193,7 @@ export namespace DragManager {
         };
     }
 
-    // drag a document and drop it (or make an alias/copy on drop)
+    // drag a document and drop it (or make an embed/copy on drop)
     export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, dropEvent?: () => any) {
         const addAudioTag = (dropDoc: any) => {
             dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField());
@@ -210,8 +210,8 @@ export namespace DragManager {
                         dragData.draggedDocuments.map(async d =>
                             !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
                                 ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
-                                : docDragData.dropAction === 'alias'
-                                ? Doc.BestAlias(d)
+                                : docDragData.dropAction === 'embed'
+                                ? Doc.BestEmbedding(d)
                                 : docDragData.dropAction === 'proto'
                                 ? Doc.GetProto(d)
                                 : docDragData.dropAction === 'copy'
@@ -487,7 +487,7 @@ export namespace DragManager {
         const moveHandler = (e: PointerEvent) => {
             e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
             if (dragData instanceof DocumentDragData) {
-                dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'alias' : dragData.defaultDropAction;
+                dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'embed' : dragData.defaultDropAction;
             }
             if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header' || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
                 if (!startWindowDragTimer) {
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 2e1d6ba23..f46ea393a 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -58,8 +58,8 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
         // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
         if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) {
             if (data.removeDropProperties || dbox.removeDropProperties) {
-                //dbox = Doc.MakeAlias(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
-                dbox = Doc.MakeAlias(dbox);
+                //dbox = Doc.MakeEmbedding(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
+                dbox = Doc.MakeEmbedding(dbox);
                 const dragProps = Cast(dbox.removeDropProperties, listSpec('string'), []);
                 const remProps = (data.removeDropProperties || []).concat(Array.from(dragProps));
                 remProps.map(prop => (dbox[prop] = undefined));
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index c8b784390..da947aba6 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -155,7 +155,7 @@ export class GroupManager extends React.Component<{}> {
     addGroup(groupDoc: Doc): boolean {
         if (this.GroupManagerDoc) {
             Doc.AddDocToList(this.GroupManagerDoc, 'data', groupDoc);
-            this.GroupManagerDoc['data-lastModified'] = new DateField();
+            this.GroupManagerDoc['data_modificationDate'] = new DateField();
             return true;
         }
         return false;
@@ -176,7 +176,7 @@ export class GroupManager extends React.Component<{}> {
                     const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group);
                     index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1);
                 }
-                this.GroupManagerDoc['data-lastModified'] = new DateField();
+                this.GroupManagerDoc['data_modificationDate'] = new DateField();
                 if (group === this.currentGroup) {
                     this.currentGroup = undefined;
                 }
@@ -197,7 +197,7 @@ export class GroupManager extends React.Component<{}> {
             !memberList.includes(email) && memberList.push(email);
             groupDoc.members = JSON.stringify(memberList);
             SharingManager.Instance.shareWithAddedMember(groupDoc, email);
-            this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
+            this.GroupManagerDoc && (this.GroupManagerDoc['data_modificationDate'] = new DateField());
         }
     }
 
@@ -214,7 +214,7 @@ export class GroupManager extends React.Component<{}> {
                 const user = memberList.splice(index, 1)[0];
                 groupDoc.members = JSON.stringify(memberList);
                 SharingManager.Instance.removeMember(groupDoc, email);
-                this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
+                this.GroupManagerDoc && (this.GroupManagerDoc['data_modificationDate'] = new DateField());
             }
         }
     }
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 297520d5d..151f18d6f 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -20,7 +20,7 @@ export namespace Hypothesis {
     export const getSourceWebDoc = async (uri: string) => {
         const result = await findWebDoc(uri);
         console.log(result ? 'existing doc found' : 'existing doc NOT found');
-        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
+        return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, data_useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
     };
 
     /**
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 9bd92a316..55d37f544 100644
--- a/src/client/util/Import & Export/ImageUtils.ts	
+++ b/src/client/util/Import & Export/ImageUtils.ts	
@@ -1,12 +1,11 @@
-import { Doc } from "../../../fields/Doc";
-import { ImageField } from "../../../fields/URLField";
-import { Cast, StrCast, NumCast } from "../../../fields/Types";
-import { Networking } from "../../Network";
-import { Id } from "../../../fields/FieldSymbols";
-import { Utils } from "../../../Utils";
+import { Doc } from '../../../fields/Doc';
+import { ImageField } from '../../../fields/URLField';
+import { Cast, StrCast, NumCast } from '../../../fields/Types';
+import { Networking } from '../../Network';
+import { Id } from '../../../fields/FieldSymbols';
+import { Utils } from '../../../Utils';
 
 export namespace ImageUtils {
-
     export const ExtractExif = async (document: Doc): Promise<boolean> => {
         const field = Cast(document.data, ImageField);
         if (!field) {
@@ -17,23 +16,22 @@ export namespace ImageUtils {
             contentSize,
             nativeWidth,
             nativeHeight,
-            exifData: { error, data }
-        } = await Networking.PostToServer("/inspectImage", { source });
+            exifData: { error, data },
+        } = await Networking.PostToServer('/inspectImage', { source });
         document.exif = error || Doc.Get.FromJson({ data });
         const proto = Doc.GetProto(document);
-        nativeWidth && (document._height = NumCast(document._width) * nativeHeight / nativeWidth);
-        proto["data-nativeWidth"] = nativeWidth;
-        proto["data-nativeHeight"] = nativeHeight;
-        proto["data-path"] = source;
-        proto.contentSize = contentSize ? contentSize : undefined;
+        nativeWidth && (document._height = (NumCast(document._width) * nativeHeight) / nativeWidth);
+        proto['data_nativeWidth'] = nativeWidth;
+        proto['data_nativeHeight'] = nativeHeight;
+        proto['data-path'] = source;
+        proto.data_contentSize = contentSize ? contentSize : undefined;
         return data !== undefined;
     };
 
     export const ExportHierarchyToFileSystem = async (collection: Doc): Promise<void> => {
-        const a = document.createElement("a");
+        const a = document.createElement('a');
         a.href = Utils.prepend(`/imageHierarchyExport/${collection[Id]}`);
         a.download = `Dash Export [${StrCast(collection.title)}].zip`;
         a.click();
     };
-
-}
\ No newline at end of file
+}
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 9591dbed3..043f0f1f3 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -161,6 +161,7 @@ export namespace InteractionUtils {
                                 <polygon
                                     style={{ stroke: color }}
                                     strokeLinejoin={lineJoin as any}
+                                    strokeDasharray={dashArray}
                                     strokeWidth={(markerStrokeWidth * 2) / 3}
                                     points={`0 ${-markerStrokeWidth * arrowWidthFactor}, ${markerStrokeWidth * arrowNotchFactor} 0, 0 ${markerStrokeWidth * arrowWidthFactor}, ${arrowLengthFactor * markerStrokeWidth} 0`}
                                 />
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index ba2edb65c..246f5bb8c 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -12,8 +12,8 @@ import { SelectionManager } from './SelectionManager';
 import { UndoManager } from './UndoManager';
 /*
  * link doc:
- * - anchor1: doc
- * - anchor2: doc
+ * - link_anchor_1: doc
+ * - link_anchor_2: doc
  *
  * group doc:
  * - type: string representing the group type/name/category
@@ -43,10 +43,10 @@ export class LinkFollower {
 
     public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) {
         const linkDocs = link ? [link] : LinkManager.Links(sourceDoc);
-        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
-        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
-        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor2 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor2 as Doc).annotationOn) : (d.anchor2 as Doc)).length === 0);
-        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor1 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor1 as Doc).annotationOn) : (d.anchor1 as Doc)).length === 0);
+        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.link_anchor_1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is link_anchor_1
+        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.link_anchor_2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.link_anchor_2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is link_anchor_2
+        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.link_anchor_2 as Doc).type === DocumentType.MARKER ? DocCast((d.link_anchor_2 as Doc).annotationOn) : (d.link_anchor_2 as Doc)).length === 0);
+        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews((d.link_anchor_1 as Doc).type === DocumentType.MARKER ? DocCast((d.link_anchor_1 as Doc).annotationOn) : (d.link_anchor_1 as Doc)).length === 0);
         const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
         const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs;
         const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1);
@@ -55,13 +55,13 @@ export class LinkFollower {
         if (!followLinks.length) finished?.();
         followLinks.forEach(async linkDoc => {
             const target = (
-                sourceDoc === linkDoc.anchor1
-                    ? linkDoc.anchor2
-                    : sourceDoc === linkDoc.anchor2
-                    ? linkDoc.anchor1
-                    : Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)
-                    ? linkDoc.anchor2
-                    : linkDoc.anchor1
+                sourceDoc === linkDoc.link_anchor_1
+                    ? linkDoc.link_anchor_2
+                    : sourceDoc === linkDoc.link_anchor_2
+                    ? linkDoc.link_anchor_1
+                    : Doc.AreProtosEqual(sourceDoc, linkDoc.link_anchor_1 as Doc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc)
+                    ? linkDoc.link_anchor_2
+                    : linkDoc.link_anchor_1
             ) as Doc;
             if (target) {
                 const doFollow = (canToggle?: boolean) => {
@@ -92,16 +92,16 @@ export class LinkFollower {
                 };
                 let movedTarget = false;
                 if (sourceDoc.followLinkLocation === OpenWhere.inParent) {
-                    const sourceDocParent = DocCast(sourceDoc.context);
-                    if (target.context instanceof Doc && target.context !== sourceDocParent) {
-                        Doc.RemoveDocFromList(target.context, Doc.LayoutFieldKey(target.context), target);
+                    const sourceDocParent = DocCast(sourceDoc.embedContainer);
+                    if (target.embedContainer instanceof Doc && target.embedContainer !== sourceDocParent) {
+                        Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutFieldKey(target.embedContainer), target);
                         movedTarget = true;
                     }
                     if (!DocListCast(sourceDocParent[Doc.LayoutFieldKey(sourceDocParent)]).includes(target)) {
                         Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target);
                         movedTarget = true;
                     }
-                    target.context = sourceDocParent;
+                    target.embedContainer = sourceDocParent;
                     const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
                     if (sourceDoc.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
                         target.x = moveTo[0];
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 555215417..5e6107e5f 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -7,8 +7,8 @@ import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
 import { ScriptingGlobals } from './ScriptingGlobals';
 /*
  * link doc:
- * - anchor1: doc
- * - anchor2: doc
+ * - link_anchor_1: doc
+ * - link_anchor_2: doc
  *
  * group doc:
  * - type: string representing the group type/name/category
@@ -33,7 +33,7 @@ export class LinkManager {
         await Promise.all(
             ((await DocListCastAsync(linkDb.data)) ?? []).map(link =>
                 // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
-                [PromiseValue(link?.anchor1), PromiseValue(link?.anchor2)]
+                [PromiseValue(link?.link_anchor_1), PromiseValue(link?.link_anchor_2)]
             )
         );
         LinkManager.userLinkDBs.push(linkDb);
@@ -42,13 +42,13 @@ export class LinkManager {
     static _links: Doc[] = [];
     constructor() {
         LinkManager._instance = this;
-        this.createLinkrelationshipLists();
+        this.createlink_relationshipLists();
         LinkManager.userLinkDBs = [];
         const addLinkToDoc = (link: Doc) => {
             Promise.all([link]).then(linkdoc => {
                 const link = DocCast(linkdoc[0]);
                 Promise.all([link.proto]).then(linkproto => {
-                    Promise.all([link.anchor1, link.anchor2]).then(linkdata => {
+                    Promise.all([link.link_anchor_1, link.link_anchor_2]).then(linkdata => {
                         const a1 = DocCast(linkdata[0]);
                         const a2 = DocCast(linkdata[1]);
                         a1 &&
@@ -64,8 +64,8 @@ export class LinkManager {
             });
         };
         const remLinkFromDoc = (link: Doc) => {
-            const a1 = link?.anchor1;
-            const a2 = link?.anchor2;
+            const a1 = link?.link_anchor_1;
+            const a2 = link?.link_anchor_2;
             Promise.all([a1, a2]).then(
                 action(() => {
                     if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
@@ -130,11 +130,11 @@ export class LinkManager {
         LinkManager.addLinkDB(Doc.LinkDBDoc());
     }
 
-    public createLinkrelationshipLists = () => {
+    public createlink_relationshipLists = () => {
         //create new lists for link relations and their associated colors if the lists don't already exist
-        !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List<string>());
+        !Doc.UserDoc().link_relationshipList && (Doc.UserDoc().link_relationshipList = new List<string>());
         !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>());
-        !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List<number>());
+        !Doc.UserDoc().link_relationshipSizes && (Doc.UserDoc().link_relationshipSizes = new List<number>());
     };
 
     public addLink(linkDoc: Doc, checkExists = false) {
@@ -162,7 +162,7 @@ export class LinkManager {
             return [];
         }
         const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
-        const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '-annotations']);
+        const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '_annotations']);
         if (!annos) debugger;
         return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
     }, true);
@@ -171,9 +171,9 @@ export class LinkManager {
     public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
         const anchorGroups = new Map<string, Array<Doc>>();
         this.relatedLinker(anchor).forEach(link => {
-            if (link.linkRelationship && link.linkRelationship !== '-ungrouped-') {
-                const relation = StrCast(link.linkRelationship);
-                const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+            if (link.link_relationship && link.link_relationship !== '-ungrouped-') {
+                const relation = StrCast(link.link_relationship);
+                const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), anchor) ? 0 : 1] : relation;
                 const group = anchorGroups.get(anchorRelation);
                 anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
             } else {
@@ -189,8 +189,8 @@ export class LinkManager {
     //TODO This should probably return undefined if there isn't an opposite anchor
     //TODO This should also await the return value of the anchor so we don't filter out promises
     public static getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
-        const a1 = Cast(linkDoc.anchor1, Doc, null);
-        const a2 = Cast(linkDoc.anchor2, Doc, null);
+        const a1 = Cast(linkDoc.link_anchor_1, Doc, null);
+        const a2 = Cast(linkDoc.link_anchor_2, Doc, null);
         if (Doc.AreProtosEqual(anchor, a1)) return a2;
         if (Doc.AreProtosEqual(anchor, a2)) return a1;
         if (Doc.AreProtosEqual(anchor, a1.annotationOn as Doc)) return a2;
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index 22cca4a2e..cbc465d6a 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -127,8 +127,8 @@ export class ReplayMovements {
     zoomAndPan = (movement: Movement, document: CollectionFreeFormView) => {
         const { panX, panY, scale } = movement;
         scale !== 0 && document.zoomSmoothlyAboutPt([panX, panY], scale, 0);
-        document.Document._panX = panX;
-        document.Document._panY = panY;
+        document.Document._freeform_panX = panX;
+        document.Document._freeform_panY = panY;
     };
 
     getFirstMovements = (movements: Movement[]): Map<Doc, Movement> => {
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 79759a71d..d154c48a4 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -24,51 +24,57 @@ export namespace SearchUtil {
 
     export interface SearchParams {
         hl?: string;
-        "hl.fl"?: string;
+        'hl.fl'?: string;
         start?: number;
         rows?: number;
         fq?: string;
         sort?: string;
-        allowAliases?: boolean;
-        onlyAliases?: boolean;
-        "facet"?: string;
-        "facet.field"?: string;
+        allowEmbeddings?: boolean;
+        onlyEmbeddings?: boolean;
+        facet?: string;
+        'facet.field'?: string;
     }
     export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
     export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
     export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
-        query = query || "*"; //If we just have a filter query, search for * as the query
-        const rpquery = Utils.prepend("/dashsearch");
+        query = query || '*'; //If we just have a filter query, search for * as the query
+        const rpquery = Utils.prepend('/dashsearch');
         let replacedQuery = query.replace(/type_t:([^ )])/g, (substring, arg) => `{!join from=id to=proto_i}*:* AND ${arg}`);
-        if (options.onlyAliases) {
-            const header = query.match(/_[atnb]?:/) ? replacedQuery : "DEFAULT:" + replacedQuery;
+        if (options.onlyEmbeddings) {
+            const header = query.match(/_[atnb]?:/) ? replacedQuery : 'DEFAULT:' + replacedQuery;
             replacedQuery = `{!join from=id to=proto_i}* AND ${header}`;
         }
         //console.log("Q: " + replacedQuery + " fq: " + options.fq);
         const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } });
-        const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
+        const result: IdSearchResult = gotten.startsWith('<') ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
         if (!returnDocs) {
             return result;
         }
 
         const { ids, highlighting } = result;
 
-        const txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), {
-            qs: { ...options, q: query.replace(/^[ \+\?\*\|]*/, "") }, // a leading '+' leads to a server crash since findInFiles doesn't handle regex failures
-        }));
+        const txtresult =
+            query !== '*' &&
+            JSON.parse(
+                await rp.get(Utils.prepend('/textsearch'), {
+                    qs: { ...options, q: query.replace(/^[ \+\?\*\|]*/, '') }, // a leading '+' leads to a server crash since findInFiles doesn't handle regex failures
+                })
+            );
 
         const fileids = txtresult ? txtresult.ids : [];
         const newIds: string[] = [];
         const newLines: string[][] = [];
-        if (fileids) {
-            await Promise.all(fileids.map(async (tr: string, i: number) => {
-                const docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query
-                const docResult = JSON.parse(await rp.get(Utils.prepend("/dashsearch"), { qs: { ...options, q: docQuery } }));
-                newIds.push(...docResult.ids);
-                newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
-            }));
-        }
-
+        // bcz: we stopped storing fileUpload id's, so this won't find anything
+        // if (fileids) {
+        //     await Promise.all(
+        //         fileids.map(async (tr: string, i: number) => {
+        //             const docQuery = 'fileUpload_t:' + tr.substr(0, 7); //If we just have a filter query, search for * as the query
+        //             const docResult = JSON.parse(await rp.get(Utils.prepend('/dashsearch'), { qs: { ...options, q: docQuery } }));
+        //             newIds.push(...docResult.ids);
+        //             newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
+        //         })
+        //     );
+        // }
 
         const theDocs: Doc[] = [];
         const theLines: string[][] = [];
@@ -86,7 +92,7 @@ export namespace SearchUtil {
         const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
         for (let i = 0; i < ids.length; i++) {
             const testDoc = docs[i];
-            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
+            if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowEmbeddings || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
                 theDocs.push(testDoc);
                 theLines.push([]);
             } else {
@@ -97,48 +103,46 @@ export namespace SearchUtil {
         return { docs: theDocs, numFound: Math.max(0, result.numFound), highlighting, lines: theLines };
     }
 
-    export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
-    export async function GetAliasesOfDocument(doc: Doc, returnDocs: false): Promise<string[]>;
-    export async function GetAliasesOfDocument(doc: Doc, returnDocs = true): Promise<Doc[] | string[]> {
+    export async function GetEmbeddingsOfDocument(doc: Doc): Promise<Doc[]>;
+    export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs: false): Promise<string[]>;
+    export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs = true): Promise<Doc[] | string[]> {
         const proto = Doc.GetProto(doc);
         const protoId = proto[Id];
         if (returnDocs) {
-            return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).docs;
+            return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).docs;
         } else {
-            return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).ids;
+            return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).ids;
         }
         // return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
     }
 
     export async function GetViewsOfDocument(doc: Doc): Promise<Doc[]> {
-        const results = await Search("", true, { fq: `proto_i:"${doc[Id]}"` });
+        const results = await Search('', true, { fq: `proto_i:"${doc[Id]}"` });
         return results.docs;
     }
 
-    export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[], aliasContexts: Doc[] }> {
-        const docContexts = (await Search("", true, { fq: `data_l:"${doc[Id]}"` })).docs;
-        const aliases = await GetAliasesOfDocument(doc, false);
-        const aliasContexts = (await Promise.all(aliases.map(doc => Search("", true, { fq: `data_l:"${doc}"` }))));
-        const contexts = { contexts: docContexts, aliasContexts: [] as Doc[] };
-        aliasContexts.forEach(result => contexts.aliasContexts.push(...result.docs));
+    export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[]; embeddingContexts: Doc[] }> {
+        const docContexts = (await Search('', true, { fq: `data_l:"${doc[Id]}"` })).docs;
+        const embeddings = await GetEmbeddingsOfDocument(doc, false);
+        const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', true, { fq: `data_l:"${doc}"` })));
+        const contexts = { contexts: docContexts, embeddingContexts: [] as Doc[] };
+        embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.docs));
         return contexts;
     }
 
-    export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[], aliasContexts: string[] }> {
-        const docContexts = (await Search("", false, { fq: `data_l:"${doc[Id]}"` })).ids;
-        const aliases = await GetAliasesOfDocument(doc, false);
-        const aliasContexts = (await Promise.all(aliases.map(doc => Search("", false, { fq: `data_l:"${doc}"` }))));
-        const contexts = { contexts: docContexts, aliasContexts: [] as string[] };
-        aliasContexts.forEach(result => contexts.aliasContexts.push(...result.ids));
+    export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[]; embeddingContexts: string[] }> {
+        const docContexts = (await Search('', false, { fq: `data_l:"${doc[Id]}"` })).ids;
+        const embeddings = await GetEmbeddingsOfDocument(doc, false);
+        const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', false, { fq: `data_l:"${doc}"` })));
+        const contexts = { contexts: docContexts, embeddingContexts: [] as string[] };
+        embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.ids));
         return contexts;
     }
 
     export async function GetAllDocs() {
-        const query = "*";
+        const query = '*';
         const response = await rp.get(Utils.prepend('/dashsearch'), {
-            qs:
-                { start: 0, rows: 10000, q: query },
-
+            qs: { start: 0, rows: 10000, q: query },
         });
         const result: IdSearchResult = JSON.parse(response);
         const { ids, numFound, highlighting } = result;
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 396d754b6..a3a924b75 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -74,7 +74,7 @@ export class SettingsManager extends React.Component<{}> {
     };
 
     @undoBatch selectUserMode = action((e: React.ChangeEvent) => (Doc.noviceMode = (e.currentTarget as any)?.value === 'Novice'));
-    @undoBatch changeShowTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().showTitle = (e.currentTarget as any).value ? 'title' : undefined));
+    @undoBatch changelayout_showTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().layout_showTitle = (e.currentTarget as any).value ? 'title' : undefined));
     @undoBatch changeFontFamily = action((e: React.ChangeEvent) => (Doc.UserDoc().fontFamily = (e.currentTarget as any).value));
     @undoBatch changeFontSize = action((e: React.ChangeEvent) => (Doc.UserDoc().fontSize = (e.currentTarget as any).value));
     @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex)));
@@ -176,7 +176,7 @@ export class SettingsManager extends React.Component<{}> {
         return (
             <div className="prefs-content">
                 <div>
-                    <input type="checkbox" onChange={e => (Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().showTitle !== undefined} />
+                    <input type="checkbox" onChange={e => (Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().layout_showTitle !== undefined} />
                     <div className="preferences-check">Show doc header</div>
                 </div>
                 <div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 4937866f8..0eecb31cf 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -172,7 +172,7 @@ export class SharingManager extends React.Component<{}> {
 
                 this.setDashboardBackground(doc, permission as SharingPermissions);
                 if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
-                else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
+                return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
             })
             .some(success => !success);
     };
@@ -215,7 +215,7 @@ export class SharingManager extends React.Component<{}> {
                     return users
                         .map(({ user, sharingDoc }) => {
                             if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
-                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc); // remove the doc from the list if it already exists
+                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
                         })
                         .some(success => !success);
                 }
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 2c0a1da8b..ed9819fc0 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -40,9 +40,9 @@ export namespace SnappingManager {
     }
 
     export function SetShowSnapLines(show: boolean) {
-        runInAction(() => (Doc.UserDoc().showSnapLines = show));
+        runInAction(() => (Doc.UserDoc().freeform_snapLines = show));
     }
     export function GetShowSnapLines() {
-        return Doc.UserDoc().showSnapLines;
+        return Doc.UserDoc().freeform_snapLines;
     }
 }
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index cb8225643..a755c5dc9 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -68,7 +68,7 @@ export class TrackMovements {
         }
 
         const disposeFunc = reaction(
-            () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0) }),
+            () => ({ x: NumCast(doc.freeform_panX, -1), y: NumCast(doc.freeform_panY, -1), scale: NumCast(doc.freeform_scale, 0) }),
             res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, doc, res.scale)
         );
         this.recordingFFViews?.set(doc, disposeFunc);
diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx
index 525c0ce5a..14c922526 100644
--- a/src/client/views/AudioWaveform.tsx
+++ b/src/client/views/AudioWaveform.tsx
@@ -1,27 +1,25 @@
-import React = require("react");
-import axios from "axios";
-import { action, computed, IReactionDisposer, reaction } from "mobx";
-import { observer } from "mobx-react";
-import Waveform from "react-audio-waveform";
-import { Doc } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { listSpec } from "../../fields/Schema";
-import { Cast } from "../../fields/Types";
-import { numberRange } from "../../Utils";
-import "./AudioWaveform.scss";
-import { Colors } from "./global/globalEnums";
-
+import React = require('react');
+import axios from 'axios';
+import { action, computed, IReactionDisposer, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import Waveform from 'react-audio-waveform';
+import { Doc } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { listSpec } from '../../fields/Schema';
+import { Cast } from '../../fields/Types';
+import { numberRange } from '../../Utils';
+import './AudioWaveform.scss';
+import { Colors } from './global/globalEnums';
 
 /**
  * AudioWaveform
- * 
+ *
  * Used in CollectionStackedTimeline to render a canvas with a visual of an audio waveform for AudioBox and VideoBox documents.
  * Uses react-audio-waveform package.
  * Bins the audio data into audioBuckets which are passed to package to render the lines.
  * Calculates new buckets each time a new zoom factor or new set of trim bounds is created and stores it in a field on the layout doc with a title indicating the bounds and zoom for that list (see audioBucketField)
  */
 
-
 export interface AudioWaveformProps {
     duration: number; // length of media clip
     rawDuration: number; // length of underlying media data
@@ -32,6 +30,7 @@ export interface AudioWaveformProps {
     zoomFactor: number;
     PanelHeight: number;
     PanelWidth: number;
+    fieldKey: string;
 }
 
 @observer
@@ -40,80 +39,71 @@ export class AudioWaveform extends React.Component<AudioWaveformProps> {
 
     _disposer: IReactionDisposer | undefined;
 
-    @computed get waveHeight() { return Math.max(50, this.props.PanelHeight); }
-
-    @computed get clipStart() { return this.props.clipStart; }
-    @computed get clipEnd() { return this.props.clipEnd; }
-    @computed get zoomFactor() { return this.props.zoomFactor; }
+    @computed get waveHeight() {
+        return Math.max(50, this.props.PanelHeight);
+    }
 
-    @computed get audioBuckets() { return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec("number"), []); }
-    audioBucketField = (start: number, end: number, zoomFactor: number) => "audioBuckets/" + "/" + start.toFixed(2).replace(".", "_") + "/" + end.toFixed(2).replace(".", "_") + "/" + (zoomFactor * 10);
+    @computed get clipStart() {
+        return this.props.clipStart;
+    }
+    @computed get clipEnd() {
+        return this.props.clipEnd;
+    }
+    @computed get zoomFactor() {
+        return this.props.zoomFactor;
+    }
 
+    @computed get audioBuckets() {
+        return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec('number'), []);
+    }
+    audioBucketField = (start: number, end: number, zoomFactor: number) => this.props.fieldKey + '_audioBuckets/' + '/' + start.toFixed(2).replace('.', '_') + '/' + end.toFixed(2).replace('.', '_') + '/' + zoomFactor * 10;
 
     componentWillUnmount() {
         this._disposer?.();
     }
 
     componentDidMount() {
-        this._disposer = reaction(() => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }),
+        this._disposer = reaction(
+            () => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }),
             ({ clipStart, clipEnd, fieldKey, zoomFactor }) => {
                 if (!this.props.layoutDoc[fieldKey]) {
                     // setting these values here serves as a "lock" to prevent multiple attempts to create the waveform at nerly the same time.
-                    const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration, 1)], listSpec("number"));
-                    this.props.layoutDoc[fieldKey] = waveform && new List<number>(waveform.slice(clipStart / this.props.rawDuration * waveform.length, clipEnd / this.props.rawDuration * waveform.length));
+                    const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration, 1)], listSpec('number'));
+                    this.props.layoutDoc[fieldKey] = waveform && new List<number>(waveform.slice((clipStart / this.props.rawDuration) * waveform.length, (clipEnd / this.props.rawDuration) * waveform.length));
                     setTimeout(() => this.createWaveformBuckets(fieldKey, clipStart, clipEnd, zoomFactor));
                 }
-            }, { fireImmediately: true });
+            },
+            { fireImmediately: true }
+        );
     }
 
     // decodes the audio file into peaks for generating the waveform
     createWaveformBuckets = async (fieldKey: string, clipStart: number, clipEnd: number, zoomFactor: number) => {
-        axios({ url: this.props.mediaPath, responseType: "arraybuffer" }).then(
-            (response) => {
-                const context = new window.AudioContext();
-                context.decodeAudioData(
-                    response.data,
-                    action((buffer) => {
-                        const rawDecodedAudioData = buffer.getChannelData(0);
-                        const startInd = clipStart / this.props.rawDuration;
-                        const endInd = clipEnd / this.props.rawDuration;
-                        const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length));
-                        const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor);
+        axios({ url: this.props.mediaPath, responseType: 'arraybuffer' }).then(response => {
+            const context = new window.AudioContext();
+            context.decodeAudioData(
+                response.data,
+                action(buffer => {
+                    const rawDecodedAudioData = buffer.getChannelData(0);
+                    const startInd = clipStart / this.props.rawDuration;
+                    const endInd = clipEnd / this.props.rawDuration;
+                    const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length));
+                    const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor);
 
-                        const bucketDataSize = Math.floor(
-                            decodedAudioData.length / numBuckets
-                        );
-                        const brange = Array.from(Array(bucketDataSize));
-                        const bucketList = numberRange(numBuckets).map(
-                            (i: number) =>
-                                brange.reduce(
-                                    (p, x, j) =>
-                                        Math.abs(
-                                            Math.max(p, decodedAudioData[i * bucketDataSize + j])
-                                        ),
-                                    0
-                                ) / 2
-                        );
-                        this.props.layoutDoc[fieldKey] = new List<number>(bucketList);
-                    })
-                );
-            }
-        );
-    }
+                    const bucketDataSize = Math.floor(decodedAudioData.length / numBuckets);
+                    const brange = Array.from(Array(bucketDataSize));
+                    const bucketList = numberRange(numBuckets).map((i: number) => brange.reduce((p, x, j) => Math.abs(Math.max(p, decodedAudioData[i * bucketDataSize + j])), 0) / 2);
+                    this.props.layoutDoc[fieldKey] = new List<number>(bucketList);
+                })
+            );
+        });
+    };
 
     render() {
         return (
             <div className="audioWaveform">
-                <Waveform
-                    color={Colors.MEDIUM_BLUE_ALT}
-                    height={this.waveHeight}
-                    barWidth={200 / this.audioBuckets.length}
-                    pos={this.props.duration}
-                    duration={this.props.duration}
-                    peaks={this.audioBuckets}
-                    progressColor={Colors.MEDIUM_BLUE_ALT}
-                />
+                <Waveform color={Colors.MEDIUM_BLUE_ALT} height={this.waveHeight} barWidth={200 / this.audioBuckets.length} pos={this.props.duration} duration={this.props.duration} peaks={this.audioBuckets} progressColor={Colors.MEDIUM_BLUE_ALT} />
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index dee161931..02c5085a2 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -358,15 +358,15 @@ export class DashboardView extends React.Component {
             y: 400,
             _width: 1500,
             _height: 1000,
-            _fitWidth: true,
-            _backgroundGridShow: true,
+            _layout_fitWidth: true,
+            _freeform_backgroundGrid: true,
             backgroundColor: background,
             title: `Untitled Tab 1`,
         };
         const title = name ? name : `Dashboard ${dashboardCount}`;
         const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
-        freeformDoc.context = dashboardDoc;
+        freeformDoc.embedContainer = dashboardDoc;
 
         // switching the tabs from the datadoc to the regular doc
         const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
@@ -396,7 +396,7 @@ export class DashboardView extends React.Component {
             btnType: ButtonType.ClickButton,
             buttonText: 'New trail',
             icon: 'plus',
-            system: true,
+            isSystem: true,
         };
         const reqdBtnScript = { onClick: `createNewPresentation()` };
         const myTrailsBtn = DocUtils.AssignScripts(Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
@@ -406,13 +406,13 @@ export class DashboardView extends React.Component {
         // but we don't want to create the list of trails here-- but rather in createDashboard
         const reqdOpts: DocumentOptions = {
             title: 'My Trails',
-            _showTitle: 'title',
+            _layout_showTitle: 'title',
             _height: 100,
             treeViewHideTitle: true,
-            _fitWidth: true,
+            _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
-            childDropAction: 'alias',
+            childDropAction: 'embed',
             treeViewTruncateTitleWidth: 150,
             ignoreClick: true,
             buttonMenu: true,
@@ -423,7 +423,7 @@ export class DashboardView extends React.Component {
             boxShadow: '0 0',
             childDontRegisterViews: true,
             targetDropAction: 'same',
-            system: true,
+            isSystem: true,
             explainer: 'All of the trails that you have created will appear here.',
         };
         const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' });
@@ -453,7 +453,7 @@ ScriptingGlobals.add(function resetDashboard(dashboard: Doc) {
     DashboardView.resetDashboard(dashboard);
 }, 'move all dashboard tabs to single stack');
 ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
-    DashboardView.openDashboard(Doc.MakeAlias(dashboard));
+    DashboardView.openDashboard(Doc.MakeEmbedding(dashboard));
 }, 'adds Dashboard to set of Dashboards');
 ScriptingGlobals.add(function snapshotDashboard() {
     DashboardView.snapshotDashboard();
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index eba55e30c..ffb2d92f6 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -133,7 +133,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
         @computed public get annotationKey() {
-            return this.fieldKey + (this._annotationKeySuffix() ? '-' + this._annotationKeySuffix() : '');
+            return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : '');
         }
 
         @action.bound
@@ -152,7 +152,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 toRemove.forEach(doc => {
                     leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                     Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
-                    doc.context = undefined;
+                    doc.embedContainer = undefined;
                     if (recent) {
                         Doc.RemoveDocFromList(recent, 'data', doc);
                         doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
@@ -193,18 +193,19 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) {
+                const aclKeys = Object.keys(this.props.Document[AclSym] ?? {});
+                aclKeys.forEach(key =>
                     added.forEach(d => {
-                        for (const key of Object.keys(this.props.Document[AclSym])) {
-                            if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d);
+                        if (d.author === denormalizeEmail(key.substring(4)) && !d.createdFrom) {
+                            distributeAcls(key, SharingPermissions.Admin, d);
                         }
-                    });
-                }
+                    })
+                );
 
                 if (effectiveAcl === AclAugment) {
                     added.map(doc => {
                         if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
-                        doc.context = this.props.Document;
+                        doc.embedContainer = this.props.Document;
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                         Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                     });
@@ -215,7 +216,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             // only make a pushpin if we have acl's to edit the document
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
-                            doc.context = this.props.Document;
+                            doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
 
                             Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
@@ -223,7 +224,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added);
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
-                    targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now()));
+                    targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(new Date(Date.now()));
                 }
             }
             return true;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 30e41b06c..547d844ca 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -178,7 +178,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             e.preventDefault();
                             let googleDoc = await Cast(dataDoc.googleDoc, Doc);
                             if (!googleDoc) {
-                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false };
+                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, data_useCors: false };
                                 googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
                                 dataDoc.googleDoc = googleDoc;
                             }
@@ -473,17 +473,17 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             </Tooltip>
         );
     }
-    @observable _aliasDown = false;
+    @observable _embedDown = false;
     onTemplateButton = action((e: React.PointerEvent): void => {
         this._tooltipOpen = false;
-        setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+        setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction);
     });
-    onAliasButtonMoved = () => {
+    onEmbedButtonMoved = () => {
         if (this._dragRef.current) {
             const dragDocView = this.view0!;
             const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
             const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            dragData.defaultDropAction = 'alias';
+            dragData.defaultDropAction = 'embed';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false });
             return true;
@@ -498,14 +498,14 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const view0 = this.view0;
         const views = this.props.views();
         return !view0 ? null : (
-            <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
+            <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embedding</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
                 <div className="documentButtonBar-linkFlyout" ref={this._dragRef} onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}>
                     <Flyout
                         anchorPoint={anchorPoints.LEFT_TOP}
-                        onOpen={action(() => (this._aliasDown = true))}
-                        onClose={action(() => (this._aliasDown = false))}
+                        onOpen={action(() => (this._embedDown = true))}
+                        onClose={action(() => (this._embedDown = false))}
                         content={
-                            !this._aliasDown ? null : (
+                            !this._embedDown ? null : (
                                 <div ref={this._ref}>
                                     {' '}
                                     <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} />
@@ -564,7 +564,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc;
             const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true);
             if (trail !== anchor.presTrail) {
-                DocUtils.MakeLink(anchor, trail, { linkRelationship: 'link trail' });
+                DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
                 anchor.presTrail = trail;
             }
             Doc.ActivePresentation = trail;
@@ -589,7 +589,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                         <LinkPopup
                             key="popup"
                             showPopup={this._showLinkPopup}
-                            linkCreated={link => (link.linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
+                            linkCreated={link => (link.layout_linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))}
                             linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)}
                             linkFrom={() => this.props.views().lastElement()?.rootDoc}
                         />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 26b64bfdd..39073d763 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -121,7 +121,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     titleFieldKey &&
                     SelectionManager.Views().forEach(d => {
                         if (titleFieldKey === 'title') {
-                            d.dataDoc['title-custom'] = !this._accumulatedTitle.startsWith('-');
+                            d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-');
                             if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
                                 Doc.RemoveDocFromList(Doc.MyPublishedDocs, undefined, d.rootDoc);
                             }
@@ -138,12 +138,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             if (curKey !== title) {
                                 if (title) {
                                     if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') {
-                                        d.rootDoc.layoutKey = `layout_${title}`;
+                                        d.rootDoc.layout_fieldKey = `layout_${title}`;
                                         d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title);
-                                        d.rootDoc[`${title}-nativeWidth`] = d.rootDoc[`${title}-nativeHeight`] = 0;
+                                        d.rootDoc[`${title}_nativeWidth`] = d.rootDoc[`${title}_nativeHeight`] = 0;
                                     }
                                 } else {
-                                    d.rootDoc.layoutKey = undefined;
+                                    d.rootDoc.layout_fieldKey = undefined;
                                 }
                             }
                         } else {
@@ -272,22 +272,22 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const selectedDocs = SelectionManager.Views();
         if (selectedDocs.length) {
             if (e.ctrlKey) {
-                // open an alias in a new tab with Ctrl Key
-                CollectionDockingView.AddSplit(Doc.BestAlias(selectedDocs[0].props.Document), OpenWhereMod.right);
+                // open an embedding in a new tab with Ctrl Key
+                CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].props.Document), OpenWhereMod.right);
             } else if (e.shiftKey) {
                 // open centered in a new workspace with Shift Key
-                const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
-                alias.context = undefined;
-                alias.x = -alias[WidthSym]() / 2;
-                alias.y = -alias[HeightSym]() / 2;
-                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: 'Tab for ' + alias.title }), OpenWhereMod.right);
+                const embedding = Doc.MakeEmbedding(selectedDocs[0].props.Document);
+                embedding.embedContainer = undefined;
+                embedding.x = -embedding[WidthSym]() / 2;
+                embedding.y = -embedding[HeightSym]() / 2;
+                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right);
             } else if (e.altKey) {
                 // open same document in new tab
                 CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, OpenWhereMod.right);
             } else {
                 var openDoc = selectedDocs[0].props.Document;
-                if (openDoc.layoutKey === 'layout_icon') {
-                    openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
+                if (openDoc.layout_fieldKey === 'layout_icon') {
+                    openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc);
                     Doc.deiconifyView(openDoc);
                 }
                 LightboxView.SetLightboxDoc(
@@ -368,8 +368,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const newloccentern = seldocview.props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]);
         const newlocenter = [newloccentern[0] - NumCast(seldocview.layoutDoc._width) / 2, newloccentern[1] - NumCast(seldocview.layoutDoc._height) / 2];
         const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI);
-        seldocview.rootDoc.rotateCenterX = final.x / NumCast(seldocview.layoutDoc._width);
-        seldocview.rootDoc.rotateCenterY = final.y / NumCast(seldocview.layoutDoc._height);
+        seldocview.rootDoc.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width);
+        seldocview.rootDoc.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height);
     };
 
     @action
@@ -386,8 +386,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             action(action(() => (this._isRotating = false))), // upEvent
             action((e, doubleTap) => {
                 if (doubleTap) {
-                    seldocview.rootDoc.rotateCenterX = 0.5;
-                    seldocview.rootDoc.rotateCenterY = 0.5;
+                    seldocview.rootDoc.rotation_centerX = 0.5;
+                    seldocview.rootDoc.rotation_centerY = 0.5;
                 }
             })
         );
@@ -586,7 +586,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     let dW = docwidth * (dWin / refWidth);
                     let dH = docheight * (dHin / refHeight);
                     const scale = docView.props.ScreenToLocalTransform().Scale;
-                    const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
+                    const modifyNativeDim = (e.ctrlKey || doc.layout_forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen);
                     if (nwidth && nheight) {
                         if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) {
                             docheight = (nheight / nwidth) * docwidth;
@@ -602,7 +602,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
                     let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
                     const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false;
-                    const fixedAspect = nwidth && nheight && (!doc._fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
+                    const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
                     if (fixedAspect) {
                         if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) {
                             if (dragRight && modifyNativeDim) {
@@ -610,7 +610,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     doc._nativeWidth = (actualdW / (docwidth || 1)) * Doc.NativeWidth(doc);
                                 }
                             } else {
-                                if (!doc._fitWidth || preserveNativeDim) {
+                                if (!doc._layout_fitWidth || preserveNativeDim) {
                                     actualdH = (nheight / nwidth) * actualdW;
                                     doc._height = actualdH;
                                 } else if (!modifyNativeDim || dragBotRight) {
@@ -619,14 +619,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             }
                             doc._width = actualdW;
                         } else {
-                            if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) {
+                            if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._layout_fitWidth))) {
                                 // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight
                                 // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match
                                 // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
                                 doc._nativeHeight = (actualdH / (docheight || 1)) * Doc.NativeHeight(doc);
-                                doc._autoHeight = false;
+                                doc._layout_autoHeight = false;
                             } else {
-                                if (!doc._fitWidth || preserveNativeDim) {
+                                if (!doc._layout_fitWidth || preserveNativeDim) {
                                     actualdW = (nwidth / nheight) * actualdH;
                                     doc._width = actualdW;
                                 } else if (!modifyNativeDim || dragBotRight) {
@@ -642,10 +642,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         const rotCtr = [docwidth / 2, docheight / 2];
                         const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
 
-                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
+                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '_scrollHeight']))) * docView.NativeDimScaling();
                         dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
-                        dH && (doc._autoHeight = false);
+                        dH && (doc._layout_autoHeight = false);
 
                         const rotCtr2 = [NumCast(doc._width) / 2, NumCast(doc._height) / 2];
                         const tlRotated2 = Utils.rotPt(-rotCtr2[0], -rotCtr2[1], (NumCast(doc._rotation) / 180) * Math.PI);
@@ -654,7 +654,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     }
                     doc.x = NumCast(doc.x) + dX;
                     doc.y = NumCast(doc.y) + dY;
-                    doc._lastModified = new DateField();
+                    doc._layout_modificationDate = new DateField();
                 }
                 const val = this._dragHeights.get(docView.layoutDoc);
                 if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) });
@@ -670,11 +670,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         this._resizeUndo?.end();
         SnappingManager.clearSnapLines();
 
-        // detect autoHeight gesture and apply
+        // detect layout_autoHeight gesture and apply
         SelectionManager.Views()
             .map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) }))
             .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
-            .forEach(pair => (pair.doc._autoHeight = true));
+            .forEach(pair => (pair.doc._layout_autoHeight = true));
         //need to change points for resize, or else rotation/control points will fail.
         this._inkDragDocs
             .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
@@ -713,7 +713,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     }
 
     @computed get hasIcons() {
-        return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === 'layout_icon');
+        return SelectionManager.Views().some(docView => docView.rootDoc.layout_fieldKey === 'layout_icon');
     }
 
     @observable _showRotCenter = false;
@@ -722,8 +722,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         if (SelectionManager.Views().length) {
             const seldocview = SelectionManager.Views()[0];
             const loccenter = Utils.rotPt(
-                NumCast(seldocview.rootDoc.rotateCenterX) * NumCast(seldocview.layoutDoc._width),
-                NumCast(seldocview.rootDoc.rotateCenterY) * NumCast(seldocview.layoutDoc._height),
+                NumCast(seldocview.rootDoc.rotation_centerX) * NumCast(seldocview.layoutDoc._width),
+                NumCast(seldocview.rootDoc.rotation_centerY) * NumCast(seldocview.layoutDoc._height),
                 (NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI
             );
             return seldocview.props
@@ -746,8 +746,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
-        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || this._isRounding || this._isRotating;
-        const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
+        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
+        const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
         const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
         const hideOpenButton =
@@ -765,7 +765,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
-                return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return (docView.rootDoc.stayInCollection && !docView.rootDoc.timelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
 
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
@@ -871,7 +871,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
-                                {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
+                                {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as new embedding, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
                                 <>
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 84b2e1407..75e0e7c4c 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -46,14 +46,14 @@ export class FilterPanel extends React.Component<filterProps> {
     @computed get _allFacets() {
         // trace();
         const noviceReqFields = ['author', 'tags', 'text', 'type'];
-        const noviceLayoutFields: string[] = []; //["_curPage"];
+        const noviceLayoutFields: string[] = []; //["_layout_curPage"];
         const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
 
         const keys = new Set<string>(noviceFields);
         this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
         const sortedKeys = Array.from(keys.keys())
             .filter(key => key[0])
-            .filter(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
+            .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
             .sort();
         noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
         return [...noviceFields, ...sortedKeys];
@@ -88,7 +88,7 @@ export class FilterPanel extends React.Component<filterProps> {
                     (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal));
                     const fieldKey = Doc.LayoutFieldKey(t);
                     const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
-                    DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
+                    DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                 });
                 subDocs = newarray;
             }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index b04769c1e..141e99c66 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -102,7 +102,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 icon: 'clipboard',
                 pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()',
                 pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)',
-                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }),
+                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, isSystem: true }),
                 backgroundColor: 'orange',
             },
             { title: 'interpret text', icon: 'font', toolType: 'inktotext', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'orange' },
@@ -125,7 +125,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
                 backgroundColor: data.backgroundColor,
                 dragFactory: data.dragFactory,
-                system: true,
+                isSystem: true,
             })
         );
     }
@@ -138,19 +138,19 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                 ignoreClick: true,
                 _lockedPosition: true,
                 title: 'buttons',
-                _autoHeight: true,
+                _layout_autoHeight: true,
                 _yMargin: 5,
                 linearViewIsExpanded: true,
                 backgroundColor: 'white',
-                system: true,
+                isSystem: true,
             });
             thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
                 _width: 300,
                 _height: 25,
-                _autoHeight: true,
+                _layout_autoHeight: true,
                 linearViewIsExpanded: true,
                 flexDirection: 'column',
-                system: true,
+                isSystem: true,
             });
             userDoc.thumbDoc = thumbDoc;
         }
@@ -916,7 +916,7 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
 
     @computed get elements() {
         const selView = GestureOverlay.DownDocView;
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1);
+        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._freeform_scale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1);
         const rect = this._overlayRef.current?.getBoundingClientRect();
         const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true);
         B.left = B.left - width / 2;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 0f8b46dbe..b9b92dd2b 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -374,10 +374,10 @@ export class KeyManager {
                             if (count === docids.length) {
                                 const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d, true)).clone : d)));
                                 if (added.length) {
-                                    added.map(doc => (doc.context = targetDataDoc));
+                                    added.map(doc => (doc.embedContainer = targetDataDoc));
                                     undoBatch(() => {
                                         targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
-                                        targetDataDoc[fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
+                                        targetDataDoc[fieldKey + '_modificationDate'] = new DateField(new Date(Date.now()));
                                     })();
                                 }
                             }
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 156825f41..d28981e17 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -50,21 +50,21 @@ export class InkStrokeProperties {
                 if (!requireCurrPoint || this._currentPoint !== -1) {
                     const doc = inkView.rootDoc;
                     if (doc.type === DocumentType.INK && doc.width && doc.height) {
-                        const ink = Cast(doc.data, InkField)?.inkData;
+                        const ink = Cast(doc.stroke, InkField)?.inkData;
                         if (ink) {
                             const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
                             const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
-                            const ptsXscale = (NumCast(doc._width) - NumCast(doc.strokeWidth)) / (oldXrange.max - oldXrange.min || 1) || 1;
-                            const ptsYscale = (NumCast(doc._height) - NumCast(doc.strokeWidth)) / (oldYrange.max - oldYrange.min || 1) || 1;
-                            const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth));
+                            const ptsXscale = (NumCast(doc._width) - NumCast(doc.stroke_width)) / (oldXrange.max - oldXrange.min || 1) || 1;
+                            const ptsYscale = (NumCast(doc._height) - NumCast(doc.stroke_width)) / (oldYrange.max - oldYrange.min || 1) || 1;
+                            const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.stroke_width));
                             if (newPoints) {
                                 const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X));
                                 const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y));
-                                doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.strokeWidth);
-                                doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth);
+                                doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.stroke_width);
+                                doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.stroke_width);
                                 doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale;
                                 doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale;
-                                Doc.GetProto(doc).data = new InkField(newPoints);
+                                Doc.GetProto(doc).stroke = new InkField(newPoints);
                                 appliedFunc = true;
                             }
                         }
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 246b887a6..d6a6a64bf 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -172,7 +172,7 @@ export class InkTranscription extends React.Component {
             if (!selected) {
                 return;
             }
-            const ctx = await Cast(selected[0].context, Doc);
+            const ctx = await Cast(selected[0].embedContainer, Doc);
             if (!ctx) {
                 return;
             }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 2fd6cc4d6..14c5dc58d 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -6,7 +6,7 @@
               point 1, tangent pt 1, tangent pt 2, point 2, point 3, tangent pt 3, ...  (Note that segment endpoints are duplicated ie Point2 = Point 3)
         brokenIndices - an array of indexes into the data field where the incoming and outgoing tangents are not constrained to be equal
         text - a text field that will be centered within a closed ink stroke
-        isInkMask - a flag that makes the ink stroke render as a mask over its collection where the stroke itself is mixBlendMode multiplied by 
+        stroke_isInkMask - a flag that makes the ink stroke render as a mask over its collection where the stroke itself is mixBlendMode multiplied by 
                the underlying collection content, and everything outside the stroke is covered by a semi-opaque dark gray mask.
 
     The coordinates of the ink data need to be mapped to the screen since ink points are not changed when the DocumentView is translated or scaled.
@@ -134,7 +134,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      *  dimmed by a semi-opaque overlay mask.
      */
     public static toggleMask = action((inkDoc: Doc) => {
-        inkDoc.isInkMask = !inkDoc.isInkMask;
+        inkDoc.stroke_isInkMask = !inkDoc.stroke_isInkMask;
     });
     @observable controlUndo: UndoManager.Batch | undefined;
     /**
@@ -241,7 +241,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     inkScaledData = () => {
         const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
-        const inkStrokeWidth = NumCast(this.rootDoc.strokeWidth, 1);
+        const inkStrokeWidth = NumCast(this.rootDoc.stroke_width, 1);
         const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2;
         const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2;
         const inkLeft = Math.min(...inkData.map(p => p.X)) - inkStrokeWidth / 2;
@@ -310,8 +310,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             .map(p => ({ X: p[0], Y: p[1] }));
         const screenHdlPts = screenPts;
 
-        const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
-        const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
+        const startMarker = StrCast(this.layoutDoc.stroke_startMarker);
+        const endMarker = StrCast(this.layoutDoc.stroke_endMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale);
         return SnappingManager.GetIsDragging() ? null : !InkStrokeProperties.Instance._controlButton ? (
             !this.props.isSelected() || InkingStroke.IsClosed(inkData) ? null : (
@@ -330,12 +330,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     screenSpaceCenterlineStrokeWidth,
                     StrCast(inkDoc.strokeLineJoin),
                     StrCast(this.layoutDoc.strokeLineCap),
-                    StrCast(inkDoc.strokeBezier),
+                    StrCast(inkDoc.stroke_bezier),
                     'none',
                     startMarker,
                     endMarker,
                     markerScale * Math.min(screenSpaceCenterlineStrokeWidth, screenInkWidth[0] / screenSpaceCenterlineStrokeWidth),
-                    StrCast(inkDoc.strokeDash),
+                    StrCast(inkDoc.stroke_dash),
                     1,
                     1,
                     '',
@@ -355,11 +355,11 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         TraceMobx();
         const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData();
 
-        const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
-        const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
+        const startMarker = StrCast(this.layoutDoc.stroke_startMarker);
+        const endMarker = StrCast(this.layoutDoc.stroke_endMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1);
         const closed = InkingStroke.IsClosed(inkData);
-        const isInkMask = BoolCast(this.layoutDoc.isInkMask);
+        const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask);
         const fillColor = isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent';
         const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color);
 
@@ -381,12 +381,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkStrokeWidth,
             StrCast(this.layoutDoc.strokeLineJoin),
             StrCast(this.layoutDoc.strokeLineCap),
-            StrCast(this.layoutDoc.strokeBezier),
+            StrCast(this.layoutDoc.stroke_bezier),
             !closed ? 'none' : fillColor === 'transparent' ? 'none' : fillColor,
             startMarker,
             endMarker,
             markerScale,
-            StrCast(this.layoutDoc.strokeDash),
+            StrCast(this.layoutDoc.stroke_dash),
             inkScaleX,
             inkScaleY,
             '',
@@ -408,7 +408,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 inkStrokeWidth + (fillColor ? (closed ? 2 : (highlightIndex ?? 0) + 2) : 2),
                 StrCast(this.layoutDoc.strokeLineJoin),
                 StrCast(this.layoutDoc.strokeLineCap),
-                StrCast(this.layoutDoc.strokeBezier),
+                StrCast(this.layoutDoc.stroke_bezier),
                 !closed ? 'none' : !isInkMask && (fillColor === 'transparent' || suppressFill) ? 'none' : fillColor,
                 '',
                 '',
@@ -444,7 +444,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                 <svg
                     className="inkStroke"
                     style={{
-                        transform: this.props.Document.isInkMask ? `rotate(-${NumCast(this.props.CollectionFreeFormDocumentView?.().Rot)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                        transform: isInkMask ? `rotate(-${NumCast(this.props.CollectionFreeFormDocumentView?.().Rot)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
                         // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
                         cursor: this.props.isSelected() ? 'default' : undefined,
                     }}
@@ -469,7 +469,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                             setContentView={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text)
                             yPadding={10}
                             xPadding={10}
-                            fieldKey={'text'}
+                            fieldKey="text"
                             fontSize={fsize}
                             dontRegisterView={true}
                             noSidebar={true}
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index c18a89481..1b309a6be 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -31,7 +31,7 @@ type LightboxSavedState = {
     panY: Opt<number>;
     scale: Opt<number>;
     scrollTop: Opt<number>;
-    layoutKey: Opt<string>;
+    layout_fieldKey: Opt<string>;
 };
 @observer
 export class LightboxView extends React.Component<LightboxViewProps> {
@@ -51,11 +51,11 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<LightboxSavedState> }[] = [];
     @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) {
         if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
-            if (this._savedState.panX !== undefined) this.LightboxDoc._panX = this._savedState.panX;
-            if (this._savedState.panY !== undefined) this.LightboxDoc._panY = this._savedState.panY;
-            if (this._savedState.scrollTop !== undefined) this.LightboxDoc._scrollTop = this._savedState.scrollTop;
-            if (this._savedState.scale !== undefined) this.LightboxDoc._viewScale = this._savedState.scale;
-            this.LightboxDoc.layoutKey = this._savedState.layoutKey;
+            if (this._savedState.panX !== undefined) this.LightboxDoc._freeform_panX = this._savedState.panX;
+            if (this._savedState.panY !== undefined) this.LightboxDoc._freeform_panY = this._savedState.panY;
+            if (this._savedState.scrollTop !== undefined) this.LightboxDoc._layout_scrollTop = this._savedState.scrollTop;
+            if (this._savedState.scale !== undefined) this.LightboxDoc._freeform_scale = this._savedState.scale;
+            this.LightboxDoc.layout_fieldKey = this._savedState.layout_fieldKey;
         }
         if (!doc) {
             this._docFilters && (this._docFilters.length = 0);
@@ -64,17 +64,17 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             MainView.Instance._exploreMode = false;
         } else {
             const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement();
-            l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
+            l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen');
             CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.());
             //TabDocView.PinDoc(doc, { hidePresBox: true });
             this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]);
             if (doc !== LightboxView.LightboxDoc) {
                 this._savedState = {
-                    layoutKey: StrCast(doc.layoutKey),
-                    panX: Cast(doc.panX, 'number', null),
-                    panY: Cast(doc.panY, 'number', null),
-                    scale: Cast(doc.viewScale, 'number', null),
-                    scrollTop: Cast(doc.scrollTop, 'number', null),
+                    layout_fieldKey: StrCast(doc.layout_fieldKey),
+                    panX: Cast(doc.freeform_panX, 'number', null),
+                    panY: Cast(doc.freeform_panY, 'number', null),
+                    scale: Cast(doc.freeform_scale, 'number', null),
+                    scrollTop: Cast(doc.layout_scrollTop, 'number', null),
                 };
             }
         }
@@ -91,7 +91,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         this._doc = doc;
         this._layoutTemplate = layoutTemplate instanceof Doc ? layoutTemplate : undefined;
         if (doc && (typeof layoutTemplate === 'string' ? layoutTemplate : undefined)) {
-            doc.layoutKey = layoutTemplate;
+            doc.layout_fieldKey = layoutTemplate;
         }
         this._docTarget = target || doc;
 
@@ -142,7 +142,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         return LightboxView.SetLightboxDoc(
             doc,
             undefined,
-            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort(
+            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort(
                 (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)
             ),
             layoutTemplate
@@ -156,17 +156,17 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
         if (targetDocView && target) {
             const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement();
-            l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
+            l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen');
             DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 });
             if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
         } else {
             if (!target && LightboxView.path.length) {
                 const saved = LightboxView._savedState;
                 if (LightboxView.LightboxDoc && saved) {
-                    LightboxView.LightboxDoc._panX = saved.panX;
-                    LightboxView.LightboxDoc._panY = saved.panY;
-                    LightboxView.LightboxDoc._viewScale = saved.scale;
-                    LightboxView.LightboxDoc._scrollTop = saved.scrollTop;
+                    LightboxView.LightboxDoc._freeform_panX = saved.panX;
+                    LightboxView.LightboxDoc._freeform_panY = saved.panY;
+                    LightboxView.LightboxDoc._freeform_scale = saved.scale;
+                    LightboxView.LightboxDoc._layout_scrollTop = saved.scrollTop;
                 }
                 const pop = LightboxView.path.pop();
                 if (pop) {
@@ -210,7 +210,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         const coll = LightboxView._docTarget;
         if (coll) {
             const fieldKey = Doc.LayoutFieldKey(coll);
-            const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
+            const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '_annotations'])];
             const links = LinkManager.Links(coll)
                 .map(link => LinkManager.getOppositeAnchor(link, coll))
                 .filter(doc => doc)
@@ -305,9 +305,9 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     title="toggle fit width"
                     onClick={e => {
                         e.stopPropagation();
-                        LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth;
+                        LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
                     }}>
-                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
+                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
                 </div>
                 <div
                     className="lightboxView-tabBtn"
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5af9a9f9a..ce29b63cd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -157,26 +157,26 @@ export class MainView extends React.Component {
                 'dataTransition',
                 'viewTransition',
                 'treeViewOpen',
-                'showSidebar',
+                'layout_showSidebar',
                 'itemIndex', // for changing slides in presentations
-                'sidebarWidthPercent',
-                'currentTimecode',
-                'timelineHeightPercent',
+                'layout_sidebarWidthPercent',
+                'layout_currentTimecode',
+                'layout_timelineHeightPercent',
                 'presStatus',
-                'panX',
-                'panY',
+                'freeform_panX',
+                'freeform_panY',
                 'overlayX',
                 'overlayY',
-                'fitWidth',
+                'layout_fitWidth',
                 'nativeWidth',
                 'nativeHeight',
-                'text-scrollHeight',
-                'text-height',
-                'hideMinimap',
-                'viewScale',
-                'scrollTop',
+                'text_scrollHeight',
+                'text_height',
+                'layout_hideMinimap',
+                'freeform_scale',
+                'layout_scrollTop',
                 'hidden',
-                'curPage',
+                'layout_curPage',
                 'viewType',
                 'chromeHidden',
                 'currentFrame',
@@ -880,8 +880,8 @@ export class MainView extends React.Component {
                 <CollectionLinearView
                     Document={Doc.MyDockedBtns}
                     DataDoc={undefined}
-                    fieldKey={'data'}
-                    dropAction={'alias'}
+                    fieldKey="data"
+                    dropAction="embed"
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
@@ -911,7 +911,7 @@ export class MainView extends React.Component {
         );
     }
     @computed get snapLines() {
-        return !SelectionManager.Views().some(dv => dv.rootDoc.showSnapLines) ? null : (
+        return !SelectionManager.Views().some(dv => dv.rootDoc.freeform_snapLines) ? null : (
             <div className="mainView-snapLines">
                 <svg style={{ width: '100%', height: '100%' }}>
                     {SnappingManager.horizSnapLines().map(l => (
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 556d9b6a0..eb0fd0c7a 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -123,9 +123,9 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                   DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                       dragComplete: e => {
                           if (!e.aborted && e.linkDocument) {
-                              Doc.GetProto(e.linkDocument).linkRelationship = 'cropped image';
+                              Doc.GetProto(e.linkDocument).link_relationship = 'cropped image';
                               Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title;
-                              Doc.GetProto(e.linkDocument).linkDisplay = false;
+                              Doc.GetProto(e.linkDocument).layout_linkDisplay = false;
                           }
                       },
                   });
@@ -153,10 +153,10 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                 annotationOn: this.props.rootDoc,
                 title: 'Annotation on ' + this.props.rootDoc.title,
             });
-            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
-            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1) + NumCast(this.props.scrollTop);
-            marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
-            marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
+            marqueeAnno.x = NumCast(this.props.docView.props.Document.freeform_panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
+            marqueeAnno.y = NumCast(this.props.docView.props.Document.freeform_panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1) + NumCast(this.props.scrollTop);
+            marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
+            marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
             anno.remove();
             savedAnnoMap.clear();
             return marqueeAnno;
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index c7a603897..f7fb443c0 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -57,7 +57,7 @@ export class PreviewCursor extends React.Component<{}> {
                                 title: url,
                                 _width: 500,
                                 _height: 300,
-                                useCors: true,
+                                data_useCors: true,
                                 x: newPoint[0],
                                 y: newPoint[1],
                             })
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index cf808f801..86230ca78 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -74,7 +74,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get maskButton() {
         return this.propertyToggleBtn(
             'Mask',
-            'isInkMask',
+            'stroke_isInkMask',
             on => (on ? 'Make plain ink' : 'Make highlight mask'),
             on => 'paint-brush',
             (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
@@ -91,7 +91,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get clustersButton() {
         return this.propertyToggleBtn(
             'Clusters',
-            '_useClusters',
+            '_freeform_useClusters',
             on => `${on ? 'Hide' : 'Show'} clusters`,
             on => 'braille'
         );
@@ -115,7 +115,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get fitContentButton() {
         return this.propertyToggleBtn(
             'View All',
-            '_fitContentsToBox',
+            '_freeform_fitContentsToBox',
             on => `${on ? "Don't" : 'Do'} fit content to container visible area`,
             on => 'object-group'
         );
@@ -136,20 +136,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     // containerDoc.noShadow =
                     // containerDoc.disableDocBrushing =
                     // containerDoc._forceActive =
-                    //containerDoc._fitContentsToBox =
+                    //containerDoc._freeform_fitContentsToBox =
                     containerDoc._isLightbox = !containerDoc._isLightbox;
                     //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
                     const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
                     //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
-                    containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.linkDisplay = false)));
+                    containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
                 });
             }
         );
     }
-    @computed get fitWidthButton() {
+    @computed get layout_fitWidthButton() {
         return this.propertyToggleBtn(
             'Fit\xA0Width',
-            '_fitWidth',
+            '_layout_fitWidth',
             on => `${on ? "Don't" : 'Do'} fit content to width of container`,
             on => 'arrows-alt-h'
         );
@@ -157,10 +157,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get captionButton() {
         return this.propertyToggleBtn(
             'Caption',
-            '_showCaption',
+            '_layout_showCaption',
             on => `${on ? 'Hide' : 'Show'} caption footer`,
             on => 'closed-captioning',
-            (dv, doc) => ((dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? 'caption' : undefined)
+            (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined)
         );
     }
     @computed get chromeButton() {
@@ -175,20 +175,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get titleButton() {
         return this.propertyToggleBtn(
             'Title',
-            '_showTitle',
+            '_layout_showTitle',
             on => 'Switch between title styles',
             on => 'text-width',
             (dv, doc) => {
                 const tdoc = dv?.rootDoc || doc;
-                const newtitle = !tdoc._showTitle ? 'title' : tdoc._showTitle === 'title' ? 'title:hover' : '';
-                tdoc._showTitle = newtitle;
+                const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : '';
+                tdoc._layout_showTitle = newtitle;
             }
         );
     }
-    @computed get autoHeightButton() {
+    @computed get layout_autoHeightButton() {
         return this.propertyToggleBtn(
             'Auto\xA0Size',
-            '_autoHeight',
+            '_layout_autoHeight',
             on => `Automatical vertical sizing to show all content`,
             on => 'arrows-alt-v'
         );
@@ -196,7 +196,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get gridButton() {
         return this.propertyToggleBtn(
             'Grid',
-            '_backgroundGridShow',
+            '_freeform_backgroundGrid',
             on => `Display background grid in collection`,
             on => 'border-all'
         );
@@ -236,7 +236,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get snapButton() {
         return this.propertyToggleBtn(
             'Snap\xA0Lines',
-            'showSnapLines',
+            'freeform_snapLines',
             on => `Display snapping lines when objects are dragged`,
             on => 'th',
             undefined,
@@ -412,12 +412,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.captionButton)}
                 {toggle(this.lockButton)}
                 {toggle(this.onClickButton)}
-                {toggle(this.fitWidthButton)}
+                {toggle(this.layout_fitWidthButton)}
                 {toggle(this.freezeThumb)}
                 {toggle(this.forceActiveButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
-                {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
+                {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
                 {toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
                 {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })}
                 {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 935d551c6..46e6fd188 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -25,7 +25,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
 
         if (otherdoc) {
             otherdoc.hidden = false;
-            this.props.addDocTab(Doc.IsPrototype(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, OpenWhere.toggleRight);
+            this.props.addDocTab(Doc.IsDocDataProto(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, OpenWhere.toggleRight);
         }
     };
 
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index c14d7ef73..12a09196e 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -23,14 +23,14 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
         if (!this.props.DocView) return [];
         const target = this.props.DocView.props.Document;
         const targetContext = this.props.DocView.props.docViewPath().lastElement()?.rootDoc;
-        const aliases = DocListCast(target.aliases);
-        const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
-        const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases));
+        const embeddings = DocListCast(target.proto_embeddings);
+        const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
+        const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings));
         const containers = containerSets.reduce((p, set) => {
             set.map(s => p.add(s));
             return p;
         }, new Set<Doc>());
-        const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.aliases));
+        const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.proto_embeddings));
         const doclayouts = Array.from(
             doclayoutSets
                 .reduce((p, set) => {
@@ -48,7 +48,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
 
     getOnClick = (col: Doc, target: Doc) => {
         if (!this.props.DocView) return;
-        col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
+        col = Doc.IsDocDataProto(col) ? Doc.MakeDelegate(col) : col;
         DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col);
     };
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 0b14c7b10..19c138a21 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -123,7 +123,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     docWidth = () => {
         if (this.selectedDoc) {
             const layoutDoc = this.selectedDoc;
-            const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._fitWidth);
+            const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._layout_fitWidth);
             if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.width - 20));
             return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20;
         } else {
@@ -141,7 +141,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     this.MAX_EMBED_HEIGHT,
                     Doc.NativeAspect(layoutDoc, undefined, true)
                         ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true)
-                        : layoutDoc._fitWidth
+                        : layoutDoc._layout_fitWidth
                         ? !Doc.NativeHeight(this.dataDoc)
                             ? NumCast(this.props.height)
                             : Math.min((this.docWidth() * Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))
@@ -199,8 +199,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get noviceFields() {
-        const noviceReqFields = ['author', 'creationDate', 'tags', '_curPage'];
-        return this.editableFields(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields);
+        const noviceReqFields = ['author', 'creationDate', 'tags', '_layout_curPage'];
+        return this.editableFields(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields);
     }
 
     @undoBatch
@@ -576,7 +576,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === 'up' ? 10 : -10));
                 break;
             case 'stk':
-                this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === 'up' ? 0.1 : -0.1));
+                this.selectedDoc && (this.selectedDoc.stroke_width = NumCast(this.selectedDoc?.stroke_width) + (dirs === 'up' ? 0.1 : -0.1));
                 break;
             case 'wid':
                 const oldWidth = NumCast(this.selectedDoc?._width);
@@ -813,25 +813,25 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get solidStk() {
-        return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === '0') ? true : false;
+        return this.selectedDoc?.color && (!this.selectedDoc?.stroke_dash || this.selectedDoc?.stroke_dash === '0') ? true : false;
     }
     @computed get dashdStk() {
-        return this.selectedDoc?.strokeDash || '';
+        return this.selectedDoc?.stroke_dash || '';
     }
     @computed get unStrokd() {
         return this.selectedDoc?.color ? true : false;
     }
     @computed get widthStk() {
-        return this.getField('strokeWidth') || '1';
+        return this.getField('stroke_width') || '1';
     }
     @computed get markScal() {
         return Number(this.getField('strokeMakerScale') || '1');
     }
     @computed get markHead() {
-        return this.getField('strokeStartMarker') || '';
+        return this.getField('stroke_startMarker') || '';
     }
     @computed get markTail() {
-        return this.getField('strokeEndMarker') || '';
+        return this.getField('stroke_endMarker') || '';
     }
     set solidStk(value) {
         this.dashdStk = '';
@@ -839,22 +839,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
     set dashdStk(value) {
         value && (this._lastDash = value) && (this.unStrokd = false);
-        this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined);
+        this.selectedDoc && (this.selectedDoc.stroke_dash = value ? this._lastDash : undefined);
     }
     set markScal(value) {
         this.selectedDoc && (this.selectedDoc.strokeMarkerScale = Number(value));
     }
     set widthStk(value) {
-        this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value));
+        this.selectedDoc && (this.selectedDoc.stroke_width = Number(value));
     }
     set unStrokd(value) {
         this.colorStk = value ? '' : this._lastLine;
     }
     set markHead(value) {
-        this.selectedDoc && (this.selectedDoc.strokeStartMarker = value);
+        this.selectedDoc && (this.selectedDoc.stroke_startMarker = value);
     }
     set markTail(value) {
-        this.selectedDoc && (this.selectedDoc.strokeEndMarker = value);
+        this.selectedDoc && (this.selectedDoc.stroke_endMarker = value);
     }
 
     @computed get stkInput() {
@@ -1151,16 +1151,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get description() {
-        return Field.toString(LinkManager.currentLink?.description as any as Field);
+        return Field.toString(LinkManager.currentLink?.link_description as any as Field);
     }
     @computed get relationship() {
-        return StrCast(LinkManager.currentLink?.linkRelationship);
+        return StrCast(LinkManager.currentLink?.link_relationship);
     }
     @observable private relationshipButtonColor: string = '';
 
     // @action
-    // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; }
-    // handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.relationship = e.target.value; }
+    // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.link_description = e.target.value; }
+    // handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.link_relationship = e.target.value; }
 
     @undoBatch
     handleDescriptionChange = action((value: string) => {
@@ -1171,9 +1171,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     });
 
     @undoBatch
-    handleLinkRelationshipChange = action((value: string) => {
+    handlelinkRelationshipChange = action((value: string) => {
         if (LinkManager.currentLink && this.selectedDoc) {
-            this.setLinkRelationshipValue(value);
+            this.setlinkRelationshipValue(value);
             return true;
         }
     });
@@ -1181,19 +1181,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @undoBatch
     setDescripValue = action((value: string) => {
         if (LinkManager.currentLink) {
-            Doc.GetProto(LinkManager.currentLink).description = value;
+            Doc.GetProto(LinkManager.currentLink).link_description = value;
             return true;
         }
     });
 
     @undoBatch
-    setLinkRelationshipValue = action((value: string) => {
+    setlinkRelationshipValue = action((value: string) => {
         if (LinkManager.currentLink) {
-            const prevRelationship = LinkManager.currentLink.linkRelationship as string;
-            LinkManager.currentLink.linkRelationship = value;
-            Doc.GetProto(LinkManager.currentLink).linkRelationship = value;
-            const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList);
-            const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
+            const prevRelationship = StrCast(LinkManager.currentLink.link_relationship);
+            LinkManager.currentLink.link_relationship = value;
+            Doc.GetProto(LinkManager.currentLink).link_relationship = value;
+            const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
+            const linkRelationshipSizes = NumListCast(Doc.UserDoc().link_relationshipSizes);
             const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
 
             // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
@@ -1264,13 +1264,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     onSelectOutRelationship = () => {
-        this.setLinkRelationshipValue(this.relationship);
+        this.setlinkRelationshipValue(this.relationship);
         document.getElementById('link_relationship_input')?.blur();
     };
 
     onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
         if (e.key === 'Enter') {
-            this.setLinkRelationshipValue(this.relationship);
+            this.setlinkRelationshipValue(this.relationship);
             document.getElementById('link_relationship_input')?.blur();
         }
     };
@@ -1283,7 +1283,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const ldoc = LinkManager.currentLink;
         const lanch = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor;
         if (ldoc && lanch) return LinkManager.getOppositeAnchor(ldoc, lanch) ?? lanch;
-        return ldoc ? DocCast(ldoc.anchor2) : ldoc;
+        return ldoc ? DocCast(ldoc.link_anchor_2) : ldoc;
     }
 
     @computed get sourceAnchor() {
@@ -1314,10 +1314,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             <input
                 autoComplete={'off'}
                 id="link_relationship_input"
-                value={StrCast(this.selectedDoc?.linkRelationship)}
+                value={StrCast(LinkManager.currentLink?.link_relationship)}
                 onKeyDown={this.onRelationshipKey}
                 onBlur={this.onSelectOutRelationship}
-                onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
+                onChange={e => this.handlelinkRelationshipChange(e.currentTarget.value)}
                 className="text"
                 type="text"
             />
@@ -1331,7 +1331,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 autoComplete="off"
                 style={{ textAlign: 'left' }}
                 id="link_description_input"
-                value={StrCast(this.selectedDoc?.description)}
+                value={Field.toString(LinkManager.currentLink?.link_description as any as Field)}
                 onKeyDown={this.onDescriptionKey}
                 onBlur={this.onSelectOutDesc}
                 onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
@@ -1406,8 +1406,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline">
                                         <p>Show link</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.linkDisplay ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'linkDisplay')}
+                                            style={{ background: !LinkManager.currentLink?.layout_linkDisplay ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_linkDisplay')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
@@ -1416,8 +1416,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
                                         <p>Auto-move anchors</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.linkAutoMove ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'linkAutoMove')}
+                                            style={{ background: !LinkManager.currentLink?.layout_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_autoMoveAnchors')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
@@ -1426,8 +1426,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
                                         <p>Display arrow</p>
                                         <button
-                                            style={{ background: !LinkManager.currentLink?.linkDisplayArrow ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleLinkProp(e, 'linkDisplayArrow')}
+                                            style={{ background: !LinkManager.currentLink?.layout_linkDisplayArrow ? '' : '#4476f7', borderRadius: 3 }}
+                                            onPointerDown={e => this.toggleLinkProp(e, 'layout_linkDisplayArrow')}
                                             onClick={e => e.stopPropagation()}
                                             className="propertiesButton">
                                             <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index b0aab968e..741e87644 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -67,15 +67,15 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             annotationOn: this.props.rootDoc,
             _width: 200,
             _height: 50,
-            _fitWidth: true,
-            _autoHeight: true,
+            _layout_fitWidth: true,
+            _layout_autoHeight: true,
             _fontSize: StrCast(Doc.UserDoc().fontSize),
             _fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
-        const link = DocUtils.MakeLink(anchor, target, { linkRelationship: 'inline comment:comment on' });
-        link && (link.linkDisplay = false);
+        const link = DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' });
+        link && (link.layout_linkDisplay = false);
 
         const taggedContent = this.docFilters()
             .filter(data => data.split(':')[0])
@@ -151,7 +151,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     };
 
     get sidebarKey() {
-        return this.props.fieldKey + '-sidebar';
+        return this.props.fieldKey + '_sidebar';
     }
     filtersHeight = () => 38;
     screenToLocalTransform = () =>
@@ -170,11 +170,11 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
-    showTitle = () => 'title';
+    layout_showTitle = () => 'title';
     setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     sortByLinkAnchorY = (a: Doc, b: Doc) => {
-        const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].anchor1).y;
-        const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].anchor1).y;
+        const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].link_anchor_1).y;
+        const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].link_anchor_1).y;
         return NumCast(ay) - NumCast(by);
     };
     render() {
@@ -208,7 +208,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 style={{
                     position: 'absolute',
                     pointerEvents: this.props.isContentActive() ? 'all' : undefined,
-                    top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === 'title' ? 15 : 0,
+                    top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._layout_showTitle) === 'title' ? 15 : 0,
                     right: 0,
                     background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
                     width: `100%`,
@@ -235,7 +235,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         isAnnotationOverlay={false}
                         select={emptyFunction}
                         NativeDimScaling={returnOne}
-                        //childShowTitle={this.showTitle}
+                        //childlayout_showTitle={this.layout_showTitle}
                         isAnyChildContentActive={returnFalse}
                         childDocumentsActive={this.props.isContentActive}
                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 192a501f2..e852d9297 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -41,9 +41,9 @@ export enum StyleProp {
     PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified
     Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background
     HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that
-    ShowCaption = 'showCaption',
+    showCaption = 'layout_showCaption',
     TitleHeight = 'titleHeight', // Height of Title area
-    ShowTitle = 'showTitle', // whether to display a title on a Document (optional :hover suffix)
+    layout_showTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix)
     JitterRotation = 'jitterRotation', // whether documents should be randomly rotated
     BorderPath = 'customBorder', // border path for document view
     FontSize = 'fontSize', // size of text font
@@ -92,11 +92,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isAnnotated = property.includes(':annotated');
     const isOpen = property.includes(':open');
     const boxBackground = property.includes(':box');
-    const fieldKey = props?.fieldKey ? props.fieldKey + '-' : isCaption ? 'caption-' : '';
+    const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : '';
     const lockedPosition = () => doc && BoolCast(doc._lockedPosition);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
-    const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
+    const layout_showTitle = () => props?.styleProvider?.(doc, props, StyleProp.layout_showTitle);
     const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
     switch (property.split(':')[0]) {
         case StyleProp.TreeViewIcon:
@@ -138,25 +138,25 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.Opacity:
             return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : Cast(doc?._opacity, 'number', Cast(doc?.opacity, 'number', null));
         case StyleProp.HideLinkButton:
-            return props?.hideLinkButton || (!selected && doc?.hideLinkButton);
+            return props?.hideLinkButton || (!selected && doc?.layout_hideLinkButton);
         case StyleProp.FontSize:
             return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?._fontSize, StrCast(Doc.UserDoc().fontSize)));
         case StyleProp.FontFamily:
             return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._fontFamily, StrCast(Doc.UserDoc().fontFamily)));
         case StyleProp.FontWeight:
             return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._fontWeight, StrCast(Doc.UserDoc().fontWeight)));
-        case StyleProp.ShowTitle:
+        case StyleProp.layout_showTitle:
             return (
                 (doc &&
                     !doc.presentationTargetDoc &&
                     !props?.LayoutTemplateString?.includes(KeyValueBox.name) &&
-                    props?.showTitle?.() !== '' &&
+                    props?.layout_showTitle?.() !== '' &&
                     StrCast(
-                        doc._showTitle,
-                        props?.showTitle?.() ||
+                        doc._layout_showTitle,
+                        props?.layout_showTitle?.() ||
                             (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.FUNCPLOT, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any)
                                 ? doc.author === Doc.CurrentUserEmail
-                                    ? StrCast(Doc.UserDoc().showTitle)
+                                    ? StrCast(Doc.UserDoc().layout_showTitle)
                                     : remoteDocHeader
                                 : '')
                     )) ||
@@ -194,19 +194,19 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                   };
         case StyleProp.JitterRotation:
             return Doc.IsComicStyle(doc) ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
-        case StyleProp.ShowCaption:
-            return doc?._viewType === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._showCaption);
+        case StyleProp.showCaption:
+            return doc?._viewType === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._layout_showCaption);
         case StyleProp.HeaderMargin:
             return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) ||
-                (doc?.type === DocumentType.RTF && !showTitle()?.includes('noMargin')) ||
+                (doc?.type === DocumentType.RTF && !layout_showTitle()?.includes('noMargin')) ||
                 doc?.type === DocumentType.LABEL) &&
-                showTitle() &&
-                !StrCast(doc?.showTitle).includes(':hover')
+                layout_showTitle() &&
+                !StrCast(doc?.layout_showTitle).includes(':hover')
                 ? 15
                 : 0;
         case StyleProp.BackgroundColor: {
             if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
-            let docColor: Opt<string> = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
+            let docColor: Opt<string> = StrCast(doc?.[fieldKey + '_backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT:
                     docColor = docColor || (darkScheme() ? '' : '');
@@ -224,7 +224,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : 'rgba(105, 105, 105, 0.432)');
                     break;
                 case DocumentType.INK:
-                    docColor = doc?.isInkMask ? 'rgba(0,0,0,0.7)' : undefined;
+                    docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined;
                     break;
                 case DocumentType.SLIDER:
                     break;
@@ -293,7 +293,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 default:
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
-                        : props?.docViewPath().lastElement()?.rootDoc._useClusters
+                        : props?.docViewPath().lastElement()?.rootDoc._freeform_useClusters
                         ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : NumCast(doc.group, -1) !== -1
                         ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
@@ -381,7 +381,7 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
         return doc._viewType === CollectionViewType.Docking ? null : (
             <>
                 {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => {
-                    DocFocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.context ?? doc?.annotationOn));
+                    DocFocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.embedContainer ?? doc?.annotationOn));
                 })}
             </>
         );
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 1c816aa05..814a95ab8 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -91,12 +91,12 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     }
     templateIsUsed = (selDoc: Doc, templateDoc: Doc) => {
         const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title);
-        return StrCast(selDoc.layoutKey) === 'layout_' + template ? 'check' : 'unchecked';
+        return StrCast(selDoc.layout_fieldKey) === 'layout_' + template ? 'check' : 'unchecked';
     };
     render() {
         TraceMobx();
         const firstDoc = this.props.docViews[0].props.Document;
-        const templateName = StrCast(firstDoc.layoutKey, 'layout').replace('layout_', '');
+        const templateName = StrCast(firstDoc.layout_fieldKey, 'layout').replace('layout_', '');
         const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data);
         const addedTypes = DocListCast(Cast(Doc.UserDoc()['template-clickFuncs'], Doc, null)?.data);
         const templateMenu: Array<JSX.Element> = [];
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 7a393a4f7..adc97bbb4 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -76,7 +76,7 @@ export class Timeline extends React.Component<FieldViewProps> {
     private get children(): Doc[] {
         const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this.props.Document.type) as any);
         if (annotatedDoc) {
-            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + '-annotations']);
+            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + '_annotations']);
         }
         return DocListCast(this.props.Document[this.props.fieldKey]);
     }
@@ -290,15 +290,15 @@ export class Timeline extends React.Component<FieldViewProps> {
     };
 
     resetView(doc: Doc) {
-        doc._panX = doc._customOriginX ?? 0;
-        doc._panY = doc._customOriginY ?? 0;
-        doc._viewScale = doc._customOriginScale ?? 1;
+        doc._freeform_panX = doc._customOriginX ?? 0;
+        doc._freeform_panY = doc._customOriginY ?? 0;
+        doc._freeform_scale = doc._customOriginScale ?? 1;
     }
 
     setView(doc: Doc) {
-        doc._customOriginX = doc._panX;
-        doc._customOriginY = doc._panY;
-        doc._customOriginScale = doc._viewScale;
+        doc._customOriginX = doc._freeform_panX;
+        doc._customOriginY = doc._freeform_panY;
+        doc._customOriginScale = doc._freeform_scale;
     }
     /**
      * zooming mechanism (increment and spacing changes)
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 25c2e68e7..2349ba786 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -1,15 +1,15 @@
-import { action, computed, intercept, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc";
-import { Copy } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { ObjectField } from "../../../fields/ObjectField";
-import { listSpec } from "../../../fields/Schema";
-import { Cast, NumCast } from "../../../fields/Types";
-import { Transform } from "../../util/Transform";
-import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe";
-import "./Track.scss";
+import { action, computed, intercept, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, Opt, DocListCastAsync } from '../../../fields/Doc';
+import { Copy } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { listSpec } from '../../../fields/Schema';
+import { Cast, NumCast } from '../../../fields/Types';
+import { Transform } from '../../util/Transform';
+import { Keyframe, KeyframeFunc, RegionData } from './Keyframe';
+import './Track.scss';
 
 interface IProps {
     node: Doc;
@@ -32,27 +32,22 @@ export class Track extends React.Component<IProps> {
     @observable private _newKeyframe: boolean = false;
     private readonly MAX_TITLE_HEIGHT = 75;
     @observable private _trackHeight = 0;
-    private primitiveWhitelist = [
-        "x",
-        "y",
-        "_width",
-        "_height",
-        "opacity",
-        "_scrollTop"
-    ];
-    private objectWhitelist = [
-        "data"
-    ];
+    private primitiveWhitelist = ['x', 'y', '_width', '_height', 'opacity', '_layout_scrollTop'];
+    private objectWhitelist = ['data'];
 
-    @computed private get regions() { return DocListCast(this.props.node.regions); }
-    @computed private get time() { return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); }
+    @computed private get regions() {
+        return DocListCast(this.props.node.regions);
+    }
+    @computed private get time() {
+        return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
+    }
 
     async componentDidMount() {
         const regions = await DocListCastAsync(this.props.node.regions);
         if (!regions) this.props.node.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff
-        //these two lines are exactly same from timeline.tsx 
+        //these two lines are exactly same from timeline.tsx
         const relativeHeight = window.innerHeight / 20;
-        runInAction(() => this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT); //for responsiveness
+        runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness
         this._timelineVisibleReaction = this.timelineVisibleReaction();
         this._currentBarXReaction = this.currentBarXReaction();
         if (DocListCast(this.props.node.regions).length === 0) this.createRegion(this.time);
@@ -71,7 +66,6 @@ export class Track extends React.Component<IProps> {
     }
     ////////////////////////////////
 
-
     getLastRegionTime = () => {
         let lastTime: number = 0;
         let lastRegion: Opt<Doc>;
@@ -83,11 +77,11 @@ export class Track extends React.Component<IProps> {
             }
         });
         return lastRegion ? lastTime + NumCast(lastRegion.duration) : 0;
-    }
+    };
 
     /**
      * keyframe save logic. Needs to be changed so it's more efficient
-     * 
+     *
      */
     @action
     saveKeyframe = async () => {
@@ -97,16 +91,18 @@ export class Track extends React.Component<IProps> {
         if (this._newKeyframe) {
             DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => {
                 this.copyDocDataToKeyFrame(kf);
-                kf.opacity = (index === 0 || index === 3) ? 0.1 : 1;
+                kf.opacity = index === 0 || index === 3 ? 0.1 : 1;
             });
             this._newKeyframe = false;
         }
         if (!kf) return;
-        if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades
+        if (kf.type === KeyframeFunc.KeyframeType.default) {
+            // only save for non-fades
             this.copyDocDataToKeyFrame(kf);
             const leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists
-            const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists 
-            if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades
+            const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists
+            if (leftkf?.type === KeyframeFunc.KeyframeType.fade) {
+                //replicating this keyframe to fades
                 const edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf);
                 edge && this.copyDocDataToKeyFrame(edge);
                 leftkf && this.copyDocDataToKeyFrame(leftkf);
@@ -124,8 +120,7 @@ export class Track extends React.Component<IProps> {
         keyframes[kfIndex] = kf;
         this.saveStateKf = undefined;
         this.saveStateRegion = undefined;
-    }
-
+    };
 
     /**
      * autocreates keyframe
@@ -136,23 +131,27 @@ export class Track extends React.Component<IProps> {
         intercept(this.props.node, change => {
             return change;
         });
-        return reaction(() => {
-            return [...this.primitiveWhitelist.map(key => this.props.node[key]), ...objects];
-        }, (changed, reaction) => {
-            //check for region 
-            const region = this.findRegion(this.time);
-            if (region !== undefined) { //if region at scrub time exist
-                const r = region as RegionData; //for some region is returning undefined... which is not the case
-                if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot 
-                    this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
+        return reaction(
+            () => {
+                return [...this.primitiveWhitelist.map(key => this.props.node[key]), ...objects];
+            },
+            (changed, reaction) => {
+                //check for region
+                const region = this.findRegion(this.time);
+                if (region !== undefined) {
+                    //if region at scrub time exist
+                    const r = region as RegionData; //for some region is returning undefined... which is not the case
+                    if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) {
+                        //basically when there is no additional keyframe at that timespot
+                        this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default);
+                    }
                 }
-            }
-        }, { fireImmediately: false });
-    }
-
-
+            },
+            { fireImmediately: false }
+        );
+    };
 
-    // @observable private _storedState:(Doc | undefined) = undefined; 
+    // @observable private _storedState:(Doc | undefined) = undefined;
     // /**
     //  * reverting back to previous state before editing on AT
     //  */
@@ -161,54 +160,62 @@ export class Track extends React.Component<IProps> {
     //     if (this._storedState) this.applyKeys(this._storedState);
     // }
 
-
     /**
      * Reaction when scrubber bar changes
      * made into function so it's easier to dispose later
-    */
+     */
     @action
     currentBarXReaction = () => {
-        return reaction(() => this.props.currentBarX, () => {
-            const regiondata = this.findRegion(this.time);
-            if (regiondata) {
-                this.props.node.hidden = false;
-                // if (!this._autoKfReaction) {
-                //     // this._autoKfReaction = this.autoCreateKeyframe(); 
-                // }
-                this.timeChange();
-            } else {
-                this.props.node.hidden = true;
-                this.props.node.opacity = 0;
-                //if (this._autoKfReaction) this._autoKfReaction(); 
+        return reaction(
+            () => this.props.currentBarX,
+            () => {
+                const regiondata = this.findRegion(this.time);
+                if (regiondata) {
+                    this.props.node.hidden = false;
+                    // if (!this._autoKfReaction) {
+                    //     // this._autoKfReaction = this.autoCreateKeyframe();
+                    // }
+                    this.timeChange();
+                } else {
+                    this.props.node.hidden = true;
+                    this.props.node.opacity = 0;
+                    //if (this._autoKfReaction) this._autoKfReaction();
+                }
             }
-        });
-    }
+        );
+    };
 
     /**
      * when timeline is visible, reaction is ran so states are reverted
      */
     @action
     timelineVisibleReaction = () => {
-        return reaction(() => {
-            return this.props.timelineVisible;
-        }, isVisible => {
-            if (isVisible) {
-                this.regions.filter(region => !region.hasData).forEach(region => {
-                    for (let i = 0; i < 4; i++) {
-                        this.copyDocDataToKeyFrame(DocListCast(region.keyframes)[i]);
-                        if (i === 0 || i === 3) { //manually inputing fades
-                            DocListCast(region.keyframes)[i].opacity = 0.1;
-                        }
-                    }
-                });
-            } else {
-                //this.revertState(); 
+        return reaction(
+            () => {
+                return this.props.timelineVisible;
+            },
+            isVisible => {
+                if (isVisible) {
+                    this.regions
+                        .filter(region => !region.hasData)
+                        .forEach(region => {
+                            for (let i = 0; i < 4; i++) {
+                                this.copyDocDataToKeyFrame(DocListCast(region.keyframes)[i]);
+                                if (i === 0 || i === 3) {
+                                    //manually inputing fades
+                                    DocListCast(region.keyframes)[i].opacity = 0.1;
+                                }
+                            }
+                        });
+                } else {
+                    //this.revertState();
+                }
             }
-        });
-    }
+        );
+    };
 
-    @observable private saveStateKf: (Doc | undefined) = undefined;
-    @observable private saveStateRegion: (Doc | undefined) = undefined;
+    @observable private saveStateKf: Doc | undefined = undefined;
+    @observable private saveStateRegion: Doc | undefined = undefined;
 
     /**w
      * when scrubber position changes. Need to edit the logic
@@ -222,9 +229,9 @@ export class Track extends React.Component<IProps> {
         }
         const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on
         if (regiondata) {
-            const leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
-            const rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists        
-            const currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
+            const leftkf: Doc | undefined = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists
+            const rightkf: Doc | undefined = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists
+            const currentkf: Doc | undefined = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe
             if (currentkf) {
                 await this.applyKeys(currentkf);
                 this.saveStateKf = currentkf;
@@ -233,10 +240,10 @@ export class Track extends React.Component<IProps> {
                 await this.interpolate(leftkf, rightkf);
             }
         }
-    }
+    };
 
     /**
-     * applying changes (when saving the keyframe) 
+     * applying changes (when saving the keyframe)
      * need to change the logic here
      */
     @action
@@ -249,73 +256,73 @@ export class Track extends React.Component<IProps> {
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
-    }
-
+    };
 
     /**
      *  calculating current keyframe, if the scrubber is right on the keyframe
      */
     @action
     calcCurrent = (region: Doc) => {
-        let currentkf: (Doc | undefined) = undefined;
+        let currentkf: Doc | undefined = undefined;
         const keyframes = DocListCast(region.keyframes!);
-        keyframes.forEach((kf) => {
+        keyframes.forEach(kf => {
             if (NumCast(kf.time) === Math.round(this.time)) currentkf = kf;
         });
         return currentkf;
-    }
-
+    };
 
     /**
-     * basic linear interpolation function 
+     * basic linear interpolation function
      */
     @action
     interpolate = async (left: Doc, right: Doc) => {
         this.primitiveWhitelist.forEach(key => {
-            if (left[key] && right[key] && typeof (left[key]) === "number" && typeof (right[key]) === "number") { //if it is number, interpolate
+            if (left[key] && right[key] && typeof left[key] === 'number' && typeof right[key] === 'number') {
+                //if it is number, interpolate
                 const dif = NumCast(right[key]) - NumCast(left[key]);
                 const deltaLeft = this.time - NumCast(left.time);
                 const ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time));
-                this.props.node[key] = NumCast(left[key]) + (dif * ratio);
-            } else { // case data 
+                this.props.node[key] = NumCast(left[key]) + dif * ratio;
+            } else {
+                // case data
                 const stored = left[key];
                 this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored;
             }
         });
-    }
+    };
 
     /**
      * finds region that corresponds to specific time (is there a region at this time?)
      * linear O(n) (maybe possible to optimize this with other Data structures?)
      */
     findRegion = (time: number) => {
-        return this.regions?.find(rd => (time >= NumCast(rd.position) && time <= (NumCast(rd.position) + NumCast(rd.duration))));
-    }
-
+        return this.regions?.find(rd => time >= NumCast(rd.position) && time <= NumCast(rd.position) + NumCast(rd.duration));
+    };
 
     /**
-     * double click on track. Signalling keyframe creation. 
+     * double click on track. Signalling keyframe creation.
      */
     @action
     onInnerDoubleClick = (e: React.MouseEvent) => {
         const inner = this._inner.current!;
         const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale);
-        this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement));
-    }
-
+        this.createRegion(KeyframeFunc.convertPixelTime(offsetX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement));
+    };
 
     /**
-     * creates a region (KEYFRAME.TSX stuff). 
+     * creates a region (KEYFRAME.TSX stuff).
      */
     @action
     createRegion = (time: number) => {
-        if (this.findRegion(time) === undefined) {  //check if there is a region where double clicking (prevents phantom regions)
+        if (this.findRegion(time) === undefined) {
+            //check if there is a region where double clicking (prevents phantom regions)
             const regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data
 
             regiondata.position = time; //set position
             const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions);
 
-            if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region
+            if (rightRegion && rightRegion.position - regiondata.position <= 4000) {
+                //edge case when there is less than default 4000 duration space between this and right region
                 regiondata.duration = rightRegion.position - regiondata.position;
             }
             if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) {
@@ -325,27 +332,30 @@ export class Track extends React.Component<IProps> {
                 return regiondata;
             }
         }
-    }
+    };
 
     @action
-    makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time 
+    makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => {
+        //Kfpos is mouse offsetX, representing time
         const trackKeyFrames = DocListCast(regiondata.keyframes);
         const existingkf = trackKeyFrames.find(TK => TK.time === time);
         if (existingkf) return existingkf;
-        //else creates a new doc. 
+        //else creates a new doc.
         const newKeyFrame: Doc = new Doc();
         newKeyFrame.time = time;
         newKeyFrame.type = type;
         this.copyDocDataToKeyFrame(newKeyFrame);
         //assuming there are already keyframes (for keeping keyframes in order, sorted by time)
         if (trackKeyFrames.length === 0) regiondata.keyframes!.push(newKeyFrame);
-        trackKeyFrames.map(kf => NumCast(kf.time)).forEach((kfTime, index) => {
-            if ((kfTime < time && index === trackKeyFrames.length - 1) || (kfTime < time && time < NumCast(trackKeyFrames[index + 1].time))) {
-                regiondata.keyframes!.splice(index + 1, 0, newKeyFrame);
-            }
-        });
+        trackKeyFrames
+            .map(kf => NumCast(kf.time))
+            .forEach((kfTime, index) => {
+                if ((kfTime < time && index === trackKeyFrames.length - 1) || (kfTime < time && time < NumCast(trackKeyFrames[index + 1].time))) {
+                    regiondata.keyframes!.splice(index + 1, 0, newKeyFrame);
+                }
+            });
         return newKeyFrame;
-    }
+    };
 
     @action
     copyDocDataToKeyFrame = (doc: Doc) => {
@@ -353,7 +363,7 @@ export class Track extends React.Component<IProps> {
             const originalVal = this.props.node[key];
             doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal;
         });
-    }
+    };
 
     /**
      * UI sstuff here. Not really much to change
@@ -362,10 +372,13 @@ export class Track extends React.Component<IProps> {
         return (
             <div className="track-container">
                 <div className="track">
-                    <div className="inner" ref={this._inner} style={{ height: `${this._trackHeight}px` }}
+                    <div
+                        className="inner"
+                        ref={this._inner}
+                        style={{ height: `${this._trackHeight}px` }}
                         onDoubleClick={this.onInnerDoubleClick}
                         onPointerOver={() => Doc.BrushDoc(this.props.node)}
-                        onPointerOut={() => Doc.UnBrushDoc(this.props.node)} >
+                        onPointerOut={() => Doc.UnBrushDoc(this.props.node)}>
                         {this.regions?.map((region, i) => {
                             return <Keyframe key={`${i}`} {...this.props} RegionData={region} makeKeyData={this.makeKeyData} />;
                         })}
@@ -374,4 +387,4 @@ export class Track extends React.Component<IProps> {
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 02f925bea..9ec78155c 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -41,16 +41,16 @@ export class CollectionCarouselView extends CollectionSubView() {
         const childValue = doc?.['caption-' + property] ? this.props.styleProvider?.(doc, captionProps, property) : undefined;
         return childValue ?? this.props.styleProvider?.(this.layoutDoc, captionProps, property);
     };
-    panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._showCaption) ? 50 : 0);
+    panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0);
     onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
     onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
     @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
         const curDoc = this.childLayoutPairs?.[index];
         const captionProps = { ...this.props, fieldKey: 'caption', setHeight: undefined };
-        const marginX = NumCast(this.layoutDoc['caption-xMargin']);
-        const marginY = NumCast(this.layoutDoc['caption-yMargin']);
-        const showCaptions = StrCast(this.layoutDoc._showCaption);
+        const marginX = NumCast(this.layoutDoc['caption_xMargin']);
+        const marginY = NumCast(this.layoutDoc['caption_yMargin']);
+        const show_captions = StrCast(this.layoutDoc._layout_showCaption);
         return !(curDoc?.layout instanceof Doc) ? null : (
             <>
                 <div className="collectionCarouselView-image" key="image">
@@ -60,7 +60,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                         NativeHeight={returnZero}
                         onDoubleClick={this.onContentDoubleClick}
                         onClick={this.onContentClick}
-                        hideCaptions={showCaptions ? true : false}
+                        hideCaptions={show_captions ? true : false}
                         renderDepth={this.props.renderDepth + 1}
                         LayoutTemplate={this.props.childLayoutTemplate}
                         LayoutTemplateString={this.props.childLayoutString}
@@ -75,13 +75,13 @@ export class CollectionCarouselView extends CollectionSubView() {
                     key="caption"
                     onWheel={StopEvent}
                     style={{
-                        display: showCaptions ? undefined : 'none',
+                        display: show_captions ? undefined : 'none',
                         borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding),
                         marginRight: marginX,
                         marginLeft: marginX,
                         width: `calc(100% - ${marginX * 2}px)`,
                     }}>
-                    <FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={showCaptions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
+                    <FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
                 </div>
             </>
         );
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index bb1f788d4..3a4691484 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -127,12 +127,12 @@ export class CollectionDockingView extends CollectionSubView() {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (instance) {
-            if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') {
+            if (doc._viewType === CollectionViewType.Docking && doc.layout_fieldKey === 'layout') {
                 return DashboardView.openDashboard(doc);
             }
             const newItemStackConfig = {
                 type: 'stack',
-                content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))],
+                content: [CollectionDockingView.makeDocumentConfig(Doc.MakeEmbedding(doc))],
             };
             const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
             instance._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -411,8 +411,8 @@ export class CollectionDockingView extends CollectionSubView() {
             const _height = Number(getComputedStyle(content).height.replace('px', ''));
             return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', (iconFile, _nativeWidth, _nativeHeight) => {
                 const proto = this.dataDoc; // Cast(img.proto, Doc, null)!;
-                proto['thumb-nativeWidth'] = _width;
-                proto['thumb-nativeHeight'] = _height;
+                proto['thumb_nativeWidth'] = _width;
+                proto['thumb_nativeHeight'] = _height;
                 proto.thumb = new ImageField(iconFile);
             });
         }
@@ -434,11 +434,11 @@ export class CollectionDockingView extends CollectionSubView() {
             .map(f => f as Doc);
         const newtabs = origtabs.map(origtab => {
             const origtabdocs = DocListCast(origtab.data);
-            const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeAlias(origtab);
-            const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeAlias(origtabdoc));
+            const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeEmbedding(origtab);
+            const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc));
             if (newtabdocs.length) {
                 Doc.GetProto(newtab).data = new List<Doc>(newtabdocs);
-                newtabdocs.forEach(ntab => (ntab.context = newtab));
+                newtabdocs.forEach(ntab => (ntab.embedContainer = newtab));
             }
             json = json.replace(origtab[Id], newtab[Id]);
             return newtab;
@@ -498,8 +498,8 @@ export class CollectionDockingView extends CollectionSubView() {
                 const docToAdd = Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(),
                     _height: this.props.PanelHeight(),
-                    _backgroundGridShow: true,
-                    _fitWidth: true,
+                    _freeform_backgroundGrid: true,
+                    _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
@@ -541,8 +541,8 @@ export class CollectionDockingView extends CollectionSubView() {
                         const docToAdd = Docs.Create.FreeformDocument([], {
                             _width: this.props.PanelWidth(),
                             _height: this.props.PanelHeight(),
-                            _fitWidth: true,
-                            _backgroundGridShow: true,
+                            _layout_fitWidth: true,
+                            _freeform_backgroundGrid: true,
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
                         this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index befd89e41..528781991 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -44,7 +44,7 @@ interface CMVFieldRowProps {
 @observer
 export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowProps> {
     @observable private _background = 'inherit';
-    @observable private _createAliasSelected: boolean = false;
+    @observable private _createEmbeddingSelected: boolean = false;
     @observable private heading: string = '';
     @observable private color: string = '#f1efeb';
     @observable private collapsed: boolean = false;
@@ -95,7 +95,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @undoBatch
     rowDrop = action((e: Event, de: DragManager.DropEvent) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         if (de.complete.docDragData) {
             this.props.parent.Document.dropConverter instanceof ScriptField && this.props.parent.Document.dropConverter.script.run({ dragData: de.complete.docDragData });
             const key = this.props.pivotField;
@@ -117,12 +117,12 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         const key = this.props.pivotField;
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.parent.columnHeaders) {
-                if (this.props.parent.columnHeaders.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+            if (this.props.parent.colHeaderData) {
+                if (this.props.parent.colHeaderData.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
                     return false;
                 }
             }
@@ -135,7 +135,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     changeColumnColor = (color: string) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         this._color = color;
     };
 
@@ -143,16 +143,16 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @action
     pointerLeaveRow = () => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         this._background = 'inherit';
     };
 
     @action
     addDocument = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         const key = this.props.pivotField;
-        const newDoc = Docs.Create.TextDocument('', { _autoHeight: true, _width: 200, _fitWidth: true, title: value });
+        const newDoc = Docs.Create.TextDocument('', { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
         const onLayoutDoc = this.onLayoutDoc(key);
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = value;
@@ -163,33 +163,33 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     deleteRow = undoBatch(
         action(() => {
-            this._createAliasSelected = false;
+            this._createEmbeddingSelected = false;
             const key = this.props.pivotField;
             this.props.docList.forEach(d => Doc.SetInPlace(d, key, undefined, true));
-            if (this.props.parent.columnHeaders && this.props.headingObject) {
-                const index = this.props.parent.columnHeaders.indexOf(this.props.headingObject);
-                this.props.parent.columnHeaders.splice(index, 1);
+            if (this.props.parent.colHeaderData && this.props.headingObject) {
+                const index = this.props.parent.colHeaderData.indexOf(this.props.headingObject);
+                this.props.parent.colHeaderData.splice(index, 1);
             }
         })
     );
 
     @action
     collapseSection = (e: any) => {
-        this._createAliasSelected = false;
+        this._createEmbeddingSelected = false;
         this.toggleVisibility();
         e.stopPropagation();
     };
 
     headerMove = (e: PointerEvent) => {
-        const alias = Doc.MakeAlias(this.props.Document);
+        const embedding = Doc.MakeEmbedding(this.props.Document);
         const key = this.props.pivotField;
         let value = this.getValue(this.heading);
         value = typeof value === 'string' ? `"${value}"` : value;
         const script = `return doc.${key} === ${value}`;
         const compiled = CompileScript(script, { params: { doc: Doc.name } });
         if (compiled.compiled) {
-            alias.viewSpecScript = new ScriptField(compiled);
-            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
+            embedding.viewSpecScript = new ScriptField(compiled);
+            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
         }
         return true;
     };
@@ -198,7 +198,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
     headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         if (e.button === 0 && !e.ctrlKey) {
             setupMoveUpEvents(this, e, this.headerMove, emptyFunction, e => !this.props.chromeHidden && this.collapseSection(e));
-            this._createAliasSelected = false;
+            this._createEmbeddingSelected = false;
         }
     };
 
@@ -242,16 +242,16 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         );
     };
 
-    toggleAlias = action(() => (this._createAliasSelected = true));
+    toggleEmbedding = action(() => (this._createEmbeddingSelected = true));
     toggleVisibility = () => (this._collapsed = !this.collapsed);
 
     renderMenu = () => {
-        const selected = this._createAliasSelected;
+        const selected = this._createEmbeddingSelected;
         return (
             <div className="collectionStackingView-optionPicker">
                 <div className="optionOptions">
-                    <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.toggleAlias}>
-                        Create Alias
+                    <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.toggleEmbedding}>
+                        Create Embedding
                     </div>
                     <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.deleteRow}>
                         Delete
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 2154016bd..615014c4a 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -108,8 +108,8 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
                 <CollectionLinearView
                     Document={selDoc}
                     DataDoc={undefined}
-                    fieldKey={'data'}
-                    dropAction={'alias'}
+                    fieldKey="data"
+                    dropAction="embed"
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
@@ -246,32 +246,32 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     _viewCommand = {
         params: ['target'],
         title: 'bookmark view',
-        script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
+        script: "self.target._freeform_panX = self['target-freeform_panX']; self.target._freeform_panY = self['target-freeform_panY']; self.target._freeform_scale = self['target_freeform_scale']; gotoFrame(self.target, self['target-currentFrame']);",
         immediate: undoBatch((source: Doc[]) => {
-            this.target._panX = 0;
-            this.target._panY = 0;
-            this.target._viewScale = 1;
+            this.target._freeform_panX = 0;
+            this.target._freeform_panY = 0;
+            this.target._freeform_scale = 1;
             this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0;
         }),
         initialize: (button: Doc) => {
-            button['target-panX'] = this.target._panX;
-            button['target-panY'] = this.target._panY;
-            button['target-viewScale'] = this.target._viewScale;
+            button['target-panX'] = this.target._freeform_panX;
+            button['target-panY'] = this.target._freeform_panY;
+            button['target-_ayout_scale'] = this.target._freeform_scale;
             button['target-currentFrame'] = this.target._currentFrame;
         },
     };
     _clusterCommand = {
         params: ['target'],
         title: 'fit content',
-        script: 'self.target._fitContentsToBox = !self.target._fitContentsToBox;',
-        immediate: undoBatch((source: Doc[]) => (this.target._fitContentsToBox = !this.target._fitContentsToBox)),
+        script: 'self.target._freeform_fitContentsToBox = !self.target._freeform_fitContentsToBox;',
+        immediate: undoBatch((source: Doc[]) => (this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox)),
         initialize: emptyFunction,
     };
     _fitContentCommand = {
         params: ['target'],
         title: 'toggle clusters',
-        script: 'self.target._useClusters = !self.target._useClusters;',
-        immediate: undoBatch((source: Doc[]) => (this.target._useClusters = !this.target._useClusters)),
+        script: 'self.target._freeform_useClusters = !self.target._freeform_useClusters;',
+        immediate: undoBatch((source: Doc[]) => (this.target._freeform_useClusters = !this.target._freeform_useClusters)),
         initialize: emptyFunction,
     };
     _saveFilterCommand = {
@@ -548,7 +548,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     @undoBatch
     @action
     startRecording = () => {
-        const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
+        const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _layout_fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
         //Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
         CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
     };
@@ -568,29 +568,29 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     }
 
     @undoBatch
-    onAlias = () => {
+    onEmbed = () => {
         if (this.selectedDoc && this.selectedDocumentView) {
             // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
             // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
             // copy.y = NumCast(this.selectedDoc.y) + 30;
             // this.selectedDocumentView.props.addDocument?.(copy);
-            const alias = Doc.MakeAlias(this.selectedDoc);
-            alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
-            alias.y = NumCast(this.selectedDoc.y) + 30;
-            this.selectedDocumentView.props.addDocument?.(alias);
+            const embedding = Doc.MakeEmbedding(this.selectedDoc);
+            embedding.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
+            embedding.y = NumCast(this.selectedDoc.y) + 30;
+            this.selectedDocumentView.props.addDocument?.(embedding);
         }
     };
-    onAliasButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+    onEmbedButtonDown = (e: React.PointerEvent): void => {
+        setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction);
     };
 
     @undoBatch
-    onAliasButtonMoved = (e: PointerEvent) => {
+    onEmbedButtonMoved = (e: PointerEvent) => {
         const contentDiv = this.selectedDocumentView?.ContentDiv;
         if (contentDiv && this.selectedDoc) {
             const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
             const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
-            dragData.defaultDropAction = 'alias';
+            dragData.defaultDropAction = 'embed';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
                 offsetX: offset[0],
@@ -603,11 +603,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     };
 
     @computed
-    get aliasButton() {
+    get embedButton() {
         const targetDoc = this.selectedDoc;
         return !targetDoc || targetDoc.type === DocumentType.PRES ? null : (
-            <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an alias'}</div>} placement="top">
-                <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: 'drag' }}>
+            <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an embedding'}</div>} placement="top">
+                <button className="antimodeMenu-button" onPointerDown={this.onEmbedButtonDown} onClick={this.onEmbed} style={{ cursor: 'drag' }}>
                     <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
                 </button>
             </Tooltip>
@@ -657,7 +657,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                     <div className="collectionViewBaseChrome">
                         {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
                         <div className="collectionMenu-divider" key="divider1"></div>
-                        {this.aliasButton}
+                        {this.embedButton}
                         {/* {this.pinButton} */}
                         {this.toggleOverlayButton}
                         <div className="collectionMenu-divider" key="divider2"></div>
@@ -691,7 +691,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
         return this.props.docView.props.Document;
     }
     @computed get dataField() {
-        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '-annotations' : '')];
+        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '_annotations' : '')];
     }
     @computed get childDocs() {
         return DocListCast(this.dataField);
@@ -798,7 +798,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                 if (doc.type === DocumentType.INK) {
                     switch (field) {
                         case 'width':
-                            doc.strokeWidth = Number(value);
+                            doc.stroke_width = Number(value);
                             break;
                         case 'color':
                             doc.color = String(value);
@@ -807,7 +807,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                             doc.fillColor = String(value);
                             break;
                         case 'dash':
-                            doc.strokeDash = value;
+                            doc.stroke_dash = value;
                     }
                 }
             })
@@ -1005,16 +1005,15 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
 
         if (Doc.noviceMode) {
             if (docs instanceof Doc) {
-                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
+                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
                 return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
-            } else {
-                const keys = new Set<string>();
-                docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-                const noviceKeys = Array.from(keys).filter(
-                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
-                );
-                return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
             }
+            const keys = new Set<string>();
+            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+            const noviceKeys = Array.from(keys).filter(
+                key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
+            );
+            return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
         }
 
         if (docs instanceof Doc) {
@@ -1125,13 +1124,13 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
 
         if (Doc.UserDoc().noviceMode) {
             if (docs instanceof Doc) {
-                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
+                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
                 return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
             } else {
                 const keys = new Set<string>();
                 docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
                 const noviceKeys = Array.from(keys).filter(
-                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
+                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
                 );
                 return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
             }
@@ -1235,9 +1234,9 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
         const dividerWidth = 4;
         const borderWidth = Number(COLLECTION_BORDER_WIDTH);
         const panelWidth = this.props.docView.props.PanelWidth();
-        const previewWidth = NumCast(this.document.schemaPreviewWidth);
+        const previewWidth = NumCast(this.document.schema_previewWidth);
         const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
-        this.document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
+        this.document.schema_previewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
     };
 
     @undoBatch
@@ -1254,7 +1253,7 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
     };
 
     render() {
-        const previewWidth = NumCast(this.document.schemaPreviewWidth);
+        const previewWidth = NumCast(this.document.schema_previewWidth);
         const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []).length > 0;
 
         return (
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 99d4d0bee..a65e23911 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -49,12 +49,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         return BoolCast(this.layoutDoc.chromeHidden);
     }
     // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
-    @computed get columnHeaders() {
-        const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
-        const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
-        if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
+    @computed get colHeaderData() {
+        const colHeaderData = Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+        const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !colHeaderData?.find(sh => sh.heading === 'unset'));
+        if (needsUnsetCategory || colHeaderData === undefined || colHeaderData.length === 0) {
             setTimeout(() => {
-                const columnHeaders = Array.from(Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null) ?? []);
+                const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
                 const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
                 if (needsUnsetCategory || columnHeaders.length === 0) {
                     columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
@@ -62,7 +62,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 }
             });
         }
-        return columnHeaders ?? ([] as SchemaHeaderField[]);
+        return colHeaderData ?? ([] as SchemaHeaderField[]);
     }
     @computed get headerMargin() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
@@ -78,7 +78,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     }
     // numGroupColumns returns the number of columns
     @computed get numGroupColumns() {
-        return this.columnHeaders.length;
+        return this.colHeaderData.length;
     }
     // PanelWidth returns the size of the total available space the view occupies
     @computed get PanelWidth() {
@@ -117,7 +117,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // (2) documentView gets unmounted as you remove it from the list
     @computed get Sections() {
         TraceMobx();
-        const columnHeaders = this.columnHeaders;
+        const columnHeaders = this.colHeaderData;
         // filter out the currently dragged docs from the child docs, since we will insert them later
         const docs = this.childDocs.filter(d => !DragManager.docsBeingDragged.includes(d));
         const sections = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
@@ -149,9 +149,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     componentDidMount() {
         super.componentDidMount?.();
         document.addEventListener('pointerup', this.removeDocDragHighlight, true);
-        this._disposers.autoHeight = reaction(
-            () => this.layoutDoc._autoHeight,
-            autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
+        this._disposers.layout_autoHeight = reaction(
+            () => this.layoutDoc._layout_autoHeight,
+            layout_autoHeight =>
+                layout_autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
         );
     }
 
@@ -230,7 +231,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 PanelHeight={height}
                 styleProvider={this.styleProvider}
                 docViewPath={this.props.docViewPath}
-                fitWidth={this.props.childFitWidth}
+                layout_fitWidth={this.props.childLayoutFitWidth}
                 isContentActive={emptyFunction}
                 onKey={this.onKeyDown}
                 //TODO: change this from a prop to a parameter passed into a function
@@ -238,12 +239,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this.props.childLayoutTemplate}
                 LayoutTemplateString={this.props.childLayoutString}
-                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
-                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
+                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
                 dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
                 dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
                 rootSelected={this.rootSelected}
-                showTitle={this.props.childShowTitle}
+                layout_showTitle={this.props.childlayout_showTitle}
                 dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
@@ -280,10 +281,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // if a note doc. Otherwise, returns the normal width (for graphs, images, etc...)
     getDocWidth(d: Doc) {
         const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
-        const existingHeader = this.columnHeaders.find(sh => sh.heading === heading);
+        const existingHeader = this.colHeaderData.find(sh => sh.heading === heading);
         const existingWidth = existingHeader?.width ? existingHeader.width : 0;
         const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
-        const width = d.fitWidth ? maxWidth : d[WidthSym]();
+        const width = d.layout_fitWidth ? maxWidth : d[WidthSym]();
         return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
     }
 
@@ -293,14 +294,14 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
-        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
-        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[WidthSym]() : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
             const docWid = this.getDocWidth(d);
             return Math.min(maxHeight, (docWid * nh) / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
@@ -318,7 +319,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         const n = headers.length;
         const curWidths = headers.reduce((sum, hdr) => sum + Math.abs(hdr.width), 0);
         const scaleFactor = 1 / curWidths;
-        this.dataDoc.columnHeaders = new List<SchemaHeaderField>(
+        this.dataDoc[this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>(
             headers.map(h => {
                 h.setWidth(Math.abs(h.width) * scaleFactor);
                 return h;
@@ -352,7 +353,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             });
             // we alter the pivot fields of the docs in case they are moved to a new column.
             const colIndex = this.getColumnFromXCoord(xCoord);
-            const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
+            const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading);
             DragManager.docsBeingDragged.forEach(d => (d[this.notetakingCategoryField] = colHeader));
             // used to notify sections to re-render
             this.docsDraggedRowCol.length = 0;
@@ -365,12 +366,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     // This function is used to know which document a column SHOULD be in while it is being dragged.
     getColumnFromXCoord = (xCoord: number): number | undefined => {
         let colIndex: number | undefined = undefined;
-        const numColumns = this.columnHeaders.length;
+        const numColumns = this.colHeaderData.length;
         const coords = [];
         let colStartXCoord = 0;
         for (let i = 0; i < numColumns; i++) {
             coords.push(colStartXCoord);
-            colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.DividerWidth;
+            colStartXCoord += this.colHeaderData[i].width * this.availableWidth + this.DividerWidth;
         }
         coords.push(this.PanelWidth);
         for (let i = 0; i < numColumns; i++) {
@@ -386,7 +387,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     getDocsFromXCoord = (xCoord: number): Doc[] => {
         const docsMatchingHeader: Doc[] = [];
         const colIndex = this.getColumnFromXCoord(xCoord);
-        const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
+        const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading);
         this.childDocs?.map(d => {
             if (d instanceof Promise) return;
             const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
@@ -401,7 +402,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.ctrlKey || docView.rootDoc._singleLine) && ['Enter'].includes(e.key)) {
+        if (docView && (e.ctrlKey || docView.rootDoc._createDocOnCR) && ['Enter'].includes(e.key)) {
             e.stopPropagation?.();
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             Doc.GetProto(newDoc).text = undefined;
@@ -436,10 +437,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                     }
                 }
             }
-        } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
-            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
+        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
         } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
@@ -468,7 +469,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                     this.onPointerMove(true, e.clientX, e.clientY);
                     docus?.map((doc: Doc) => this.addDocument(doc));
                     const newDoc = this.childDocs.lastElement();
-                    const colHeader = colInd === undefined ? 'unset' : StrCast(this.columnHeaders[colInd].heading);
+                    const colHeader = colInd === undefined ? 'unset' : StrCast(this.colHeaderData[colInd].heading);
                     newDoc[this.notetakingCategoryField] = colHeader;
                     const docs = this.childDocList;
                     if (docs && targInd !== -1) {
@@ -502,7 +503,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                         this.refList.push(ref);
                         this.observer = new _global.ResizeObserver(
                             action((entries: any) => {
-                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                                     const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
                                     if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
                                         this.props.setHeight?.(height);
@@ -516,7 +517,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 select={this.props.select}
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
-                columnHeaders={this.columnHeaders}
+                colHeaderData={this.colHeaderData}
                 Document={this.props.Document}
                 DataDoc={this.props.DataDoc}
                 resizeColumns={this.resizeColumns}
@@ -524,6 +525,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 numGroupColumns={this.numGroupColumns}
                 gridGap={this.gridGap}
                 pivotField={this.notetakingCategoryField}
+                fieldKey={this.fieldKey}
                 dividerWidth={this.DividerWidth}
                 maxColWidth={this.maxColWidth}
                 availableWidth={this.availableWidth}
@@ -546,15 +548,15 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @undoBatch
     @action
     addGroup = (value: string) => {
-        if (this.columnHeaders) {
-            for (const header of this.columnHeaders) {
+        if (this.colHeaderData) {
+            for (const header of this.colHeaderData) {
                 if (header.heading === value) {
                     alert('You cannot use an existing column name. Please try a new column name');
                     return value;
                 }
             }
         }
-        const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
+        const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         const newColWidth = 1 / (this.numGroupColumns + 1);
         columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
         value && this.resizeColumns(columnHeaders);
@@ -566,7 +568,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
             subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
-            subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+            subItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
             subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
             ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
         }
@@ -576,8 +578,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     @action
     setColumnStartXCoords = (movementXScreen: number, colIndex: number) => {
         const movementX = this.props.ScreenToLocalTransform().transformDirection(movementXScreen, 0)[0];
-        const leftHeader = this.columnHeaders[colIndex];
-        const rightHeader = this.columnHeaders[colIndex + 1];
+        const leftHeader = this.colHeaderData[colIndex];
+        const rightHeader = this.colHeaderData[colIndex + 1];
         leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
         rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
     };
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 28bdd0cb9..63becac1e 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -29,8 +29,9 @@ interface CSVFieldColumnProps {
     docList: Doc[];
     heading: string;
     pivotField: string;
+    fieldKey: string | undefined;
     chromeHidden?: boolean;
-    columnHeaders: SchemaHeaderField[] | undefined;
+    colHeaderData: SchemaHeaderField[] | undefined;
     headingObject: SchemaHeaderField | undefined;
     yMargin: number;
     numGroupColumns: number;
@@ -61,9 +62,9 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
 
     // columnWidth returns the width of a column in absolute pixels
     @computed get columnWidth() {
-        if (!this.props.columnHeaders || !this.props.headingObject || this.props.columnHeaders.length === 1) return '100%';
-        const i = this.props.columnHeaders.indexOf(this.props.headingObject);
-        return this.props.columnHeaders[i].width * 100 + '%';
+        if (!this.props.colHeaderData || !this.props.headingObject || this.props.colHeaderData.length === 1) return '100%';
+        const i = this.props.colHeaderData.indexOf(this.props.headingObject);
+        return this.props.colHeaderData[i].width * 100 + '%';
     }
 
     private dropDisposer?: DragManager.DragDropDisposer;
@@ -105,7 +106,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     headingChanged = (value: string, shiftDown?: boolean) => {
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
+            if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
                 return false;
             }
             this.props.docList.forEach(d => (d[this.props.pivotField] = castedValue));
@@ -127,7 +128,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
         const key = this.props.pivotField;
-        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true });
+        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
         const colValue = this.getValue(this.props.heading);
         newDoc[key] = colValue;
         FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -141,11 +142,11 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
     @undoBatch
     @action
     deleteColumn = () => {
-        const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null));
+        const colHdrData = Array.from(Cast(this.props.Document[this.props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         if (this.props.headingObject) {
             this.props.docList.forEach(d => (d[this.props.pivotField] = undefined));
-            columnHeaders.splice(columnHeaders.indexOf(this.props.headingObject), 1);
-            this.props.resizeColumns(columnHeaders);
+            colHdrData.splice(colHdrData.indexOf(this.props.headingObject), 1);
+            this.props.resizeColumns(colHdrData);
         }
     };
 
@@ -211,7 +212,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
         !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true });
+            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
             if (created) {
                 if (this.props.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.Document);
@@ -243,7 +244,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
                     style={{ background: evContents !== `No Value` ? this._color : 'inherit' }}>
                     <EditableView GetValue={() => evContents} isEditingCallback={isEditing => isEditing && this.props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
                 </div>
-                {(this.props.columnHeaders?.length ?? 0) > 1 && (
+                {(this.props.colHeaderData?.length ?? 0) > 1 && (
                     <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
                         <FontAwesomeIcon icon="trash" size="lg" />
                     </button>
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 5b96a8682..ea0fbbc54 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -82,8 +82,8 @@ export class CollectionPileView extends CollectionSubView() {
             this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
             this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
             DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
-            this.layoutDoc._panX = 0;
-            this.layoutDoc._panY = -10;
+            this.layoutDoc._freeform_panX = 0;
+            this.layoutDoc._freeform_panY = -10;
             this.props.Document._pileLayoutEngine = computePassLayout.name;
         } else {
             const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500);
@@ -91,7 +91,7 @@ export class CollectionPileView extends CollectionSubView() {
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2;
             this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym]();
             this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym]();
-            this.layoutDoc._panX = this.layoutDoc._panY = 0;
+            this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
             this.layoutDoc._width = this.layoutDoc._height = defaultSize;
             this.props.Document._pileLayoutEngine = computeStarburstLayout.name;
         }
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 22a575989..6b4c8a3e9 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -24,7 +24,7 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { AudioWaveform } from '../AudioWaveform';
 import { CollectionSubView } from '../collections/CollectionSubView';
 import { LightboxView } from '../LightboxView';
-import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView';
 import { LabelBox } from '../nodes/LabelBox';
 import { VideoBox } from '../nodes/VideoBox';
 import './CollectionStackedTimeline.scss';
@@ -41,7 +41,9 @@ export type CollectionStackedTimelineProps = {
     mediaPath: string;
     dictationKey: string;
     rawDuration: number;
+    dataFieldKey: string;
     fieldKey: string;
+    thumbnails?: () => string[];
 };
 
 // trimming state: shows full clip, current trim bounds, or not trimming
@@ -80,6 +82,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     get minTrimLength() {
         return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5);
     }
+    @computed get thumbnails() {
+        return this.props.thumbnails?.();
+    }
 
     @computed get trimStart() {
         return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart;
@@ -102,7 +107,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     }
 
     @computed get currentTime() {
-        return NumCast(this.layoutDoc._currentTimecode);
+        return NumCast(this.layoutDoc._layout_currentTimecode);
     }
 
     @computed get zoomFactor() {
@@ -219,12 +224,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     };
 
     getLinkData(l: Doc) {
-        let la1 = l.anchor1 as Doc;
-        let la2 = l.anchor2 as Doc;
+        let la1 = l.link_anchor_1 as Doc;
+        let la2 = l.link_anchor_2 as Doc;
         const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag]));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
-            la1 = l.anchor2 as Doc;
-            la2 = l.anchor1 as Doc;
+            la1 = l.link_anchor_2 as Doc;
+            la2 = l.link_anchor_1 as Doc;
         }
         return { la1, la2, linkTime };
     }
@@ -292,10 +297,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         const clientX = e.clientX;
         if (rect) {
             this._hoverTime = this.toTimeline(clientX - rect.x, rect.width);
-            if (this.dataDoc.thumbnails) {
+            if (this.thumbnails) {
                 const nearest = Math.floor((this._hoverTime / this.props.rawDuration) * VideoBox.numThumbnails);
-                const thumbnails = StrListCast(this.dataDoc.thumbnails);
-                const imgField = thumbnails?.length > 0 ? new ImageField(thumbnails[nearest]) : undefined;
+                const imgField = this.thumbnails.length > 0 ? new ImageField(this.thumbnails[nearest]) : undefined;
                 this._thumbnail = imgField?.url?.href ? imgField.url.href.replace('.png', '_m.png') : undefined;
             }
         }
@@ -401,11 +405,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
                 _minFontSize: 12,
                 _maxFontSize: 24,
-                _singleLine: true,
                 _stayInCollection: true,
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
-                useLinkSmallAnchor: true,
-                hideLinkButton: true,
+                layout_hideLinkButton: true,
                 onClick: FollowLinkScript(),
                 annotationOn: rootDoc,
                 _timelineLabel: true,
@@ -434,7 +436,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 this.scrollToTime(seekTimeInSeconds);
             }
         } else {
-            if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) {
+            if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) && endTime > NumCast(this.layoutDoc._layout_currentTimecode)) {
                 if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) {
                     this.props.Pause();
                 } else {
@@ -455,7 +457,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         }
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
         const endTime = this.anchorEnd(anchorDoc);
-        if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
+        if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._layout_currentTimecode) - 1e-4) {
             if (this.props.playing()) this.props.Pause();
             else if (this.layoutDoc.autoPlayAnchors) this.props.Play();
             else if (!this.layoutDoc.autoPlayAnchors) {
@@ -596,6 +598,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         {!this.props.PanelHeight() ? null : (
                             <AudioWaveform
                                 rawDuration={this.props.rawDuration}
+                                fieldKey={this.props.dataFieldKey}
                                 duration={this.clipDuration}
                                 mediaPath={this.props.mediaPath}
                                 layoutDoc={this.layoutDoc}
@@ -661,6 +664,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
 interface StackedTimelineAnchorProps {
     mark: Doc;
+    whenChildContentsActiveChanged: (isActive: boolean) => void;
+    addDocTab: (doc: Doc, where: OpenWhere) => boolean;
     rangeClickScript: () => ScriptField;
     rangePlayScript: () => ScriptField;
     left: number;
@@ -709,7 +714,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             () => this.props.currentTimecode(),
             time => {
                 const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null);
-                const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc);
+                const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc);
                 if (
                     !LightboxView.LightboxDoc &&
                     // bcz: when should links be followed?  we don't want to move away from the video to follow a link but we can open it in a sidebar/etc.  But we don't know that upfront.
@@ -808,11 +813,9 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     isDocumentActive={this.props.isDocumentActive}
                     PanelWidth={width}
                     PanelHeight={height}
-                    fitWidth={returnTrue}
+                    layout_fitWidth={returnTrue}
                     ScreenToLocalTransform={screenXf}
-                    addDocTab={returnFalse}
                     pinToPres={emptyFunction}
-                    whenChildContentsActiveChanged={emptyFunction}
                     focus={focusFunc}
                     isContentActive={returnFalse}
                     searchFilterDocs={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index eedf639aa..404ca5d65 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -49,7 +49,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     // Not sure what a pivot field is. Seems like we cause reaction in MobX get rid of it once we exit this view
     _pivotFieldDisposer?: IReactionDisposer;
     // Seems like we cause reaction in MobX get rid of our height once we exit this view
-    _autoHeightDisposer?: IReactionDisposer;
+    _layout_autoHeightDisposer?: IReactionDisposer;
     // keeping track of documents. Updated on internal and external drops. What's the difference?
     _docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
     // Doesn't look like this field is being used anywhere. Obsolete?
@@ -65,8 +65,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
     }
     // it looks like this gets the column headers that Mehek was showing just now
-    @computed get columnHeaders() {
-        return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null);
+    @computed get colHeaderData() {
+        return Cast(this.layoutDoc['_' + this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
     }
     // Still not sure what a pivot is, but it appears that we can actually filter docs somehow?
     @computed get pivotField() {
@@ -116,10 +116,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     constructor(props: any) {
         super(props);
 
-        if (this.columnHeaders === undefined) {
+        if (this.colHeaderData === undefined) {
             // TODO: what is a layout doc? Is it literally how this document is supposed to be layed out?
             // here we're making an empty list of column headers (again, what Mehek showed us)
-            this.layoutDoc._columnHeaders = new List<SchemaHeaderField>();
+            this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
         }
     }
 
@@ -154,14 +154,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     //TODO: this seems important
     get Sections() {
         // appears that pivot field IS actually for sorting
-        if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
+        if (!this.pivotField || this.colHeaderData instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
 
-        if (this.columnHeaders === undefined) {
-            setTimeout(() => (this.layoutDoc._columnHeaders = new List<SchemaHeaderField>()), 0);
+        if (this.colHeaderData === undefined) {
+            setTimeout(() => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>()), 0);
             return new Map<SchemaHeaderField, Doc[]>();
         }
-        const columnHeaders = Array.from(this.columnHeaders);
-        const fields = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+        const colHeaderData = Array.from(this.colHeaderData);
+        const fields = new Map<SchemaHeaderField, Doc[]>(colHeaderData.map(sh => [sh, []] as [SchemaHeaderField, []]));
         let changed = false;
         this.filteredChildren.map(d => {
             const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object;
@@ -170,13 +170,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
 
             // look for if header exists already
-            const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
+            const existingHeader = colHeaderData.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
             if (existingHeader) {
                 fields.get(existingHeader)!.push(d);
             } else {
                 const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
-                columnHeaders.push(newSchemaHeader);
+                colHeaderData.push(newSchemaHeader);
                 changed = true;
             }
         });
@@ -187,13 +187,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 .filter(key => !fields.get(key)!.length)
                 .map(header => {
                     fields.delete(header);
-                    columnHeaders.splice(columnHeaders.indexOf(header), 1);
+                    colHeaderData.splice(colHeaderData.indexOf(header), 1);
                     changed = true;
                 });
         }
         changed &&
             setTimeout(
-                action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)),
+                action(() => this.colHeaderData?.splice(0, this.colHeaderData.length, ...colHeaderData)),
                 0
             );
         return fields;
@@ -206,12 +206,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         // reset section headers when a new filter is inputted
         this._pivotFieldDisposer = reaction(
             () => this.pivotField,
-            () => (this.layoutDoc._columnHeaders = new List())
+            () => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List())
         );
-        this._autoHeightDisposer = reaction(
-            () => this.layoutDoc._autoHeight,
-            autoHeight =>
-                autoHeight &&
+        this._layout_autoHeightDisposer = reaction(
+            () => this.layoutDoc._layout_autoHeight,
+            layout_autoHeight =>
+                layout_autoHeight &&
                 this.props.setHeight?.(
                     Math.min(
                         NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
@@ -224,7 +224,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     componentWillUnmount() {
         super.componentWillUnmount();
         this._pivotFieldDisposer?.();
-        this._autoHeightDisposer?.();
+        this._layout_autoHeightDisposer?.();
     }
 
     isAnyChildContentActive = () => this.props.isAnyChildContentActive();
@@ -282,12 +282,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         if (docView && ['Enter'].includes(e.key) && e.ctrlKey) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== 'Tab';
-            const layoutKey = StrCast(docView.LayoutFieldKey);
+            const layout_fieldKey = StrCast(docView.LayoutFieldKey);
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
-                newDoc[layoutKey] = docView.rootDoc[layoutKey];
+            if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
+                newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -327,19 +327,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 pointerEvents={this.props.DocumentView?.().props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
                 styleProvider={this.styleProvider}
                 docViewPath={this.props.docViewPath}
-                fitWidth={this.props.childFitWidth}
+                layout_fitWidth={this.props.childLayoutFitWidth}
                 isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
                 onBrowseClick={this.props.onBrowseClick}
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this.props.childLayoutTemplate}
                 LayoutTemplateString={this.props.childLayoutString}
-                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
-                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
+                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
                 dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
-                dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't autoHeight resize if dontRegisterView is set, but they need to.
+                dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
                 rootSelected={this.rootSelected}
-                showTitle={this.props.childShowTitle}
+                layout_showTitle={this.props.childlayout_showTitle}
                 dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
@@ -376,7 +376,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         if (!d) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const maxWidth = this.columnWidth / this.numGroupColumns;
-        if (!this.layoutDoc._columnsFill && !(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d))) {
+        if (!this.layoutDoc._columnsFill && !(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d))) {
             return Math.min(d[WidthSym](), maxWidth);
         }
         return maxWidth;
@@ -386,15 +386,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
         const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
         const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
-        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
-        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
+        const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[WidthSym]() : 0);
+        const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
             const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
             return Math.min(maxHeight, (docWid * nh) / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
@@ -460,10 +460,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     docs.splice(insertInd - offset, 0, ...newDocs);
                 }
             }
-        } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
-            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
+        } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
             e.stopPropagation();
         } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
@@ -526,7 +526,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         this.refList.push(ref);
                         this.observer = new _global.ResizeObserver(
                             action((entries: any) => {
-                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                                     const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
                                     if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
                                         this.props.setHeight?.(height);
@@ -539,7 +539,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 }}
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
-                columnHeaders={this.columnHeaders}
+                colHeaderData={this.colHeaderData}
                 Document={this.props.Document}
                 DataDoc={this.props.DataDoc}
                 renderChildren={this.children}
@@ -581,7 +581,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         this.refList.push(ref);
                         this.observer = new _global.ResizeObserver(
                             action((entries: any) => {
-                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                                     const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
                                     this.props.setHeight?.(this.headerMargin + height);
                                 }
@@ -608,9 +608,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     /// add a new group category (column) to the active set of note categories.  (e.g., if the pivot field is 'transportation', groups might be 'car', 'plane', 'bike', etc)
     @action
     addGroup = (value: string) => {
-        if (value && this.columnHeaders) {
+        if (value && this.colHeaderData) {
             const schemaHdrField = new SchemaHeaderField(value);
-            this.columnHeaders.push(schemaHdrField);
+            this.colHeaderData.push(schemaHdrField);
             return true;
         }
         return false;
@@ -630,7 +630,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const options = cm.findByDescription('Options...');
             const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
             optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
-            optionItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+            optionItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
             optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' });
             !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
         }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index d62c4dc62..243550c0b 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -34,7 +34,7 @@ interface CSVFieldColumnProps {
     heading: string;
     pivotField: string;
     chromeHidden?: boolean;
-    columnHeaders: SchemaHeaderField[] | undefined;
+    colHeaderData: SchemaHeaderField[] | undefined;
     headingObject: SchemaHeaderField | undefined;
     yMargin: number;
     columnWidth: number;
@@ -93,7 +93,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @undoBatch
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
         const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
-        drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
+        this.props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
     });
     getValue = (value: string): any => {
         const parsed = parseInt(value);
@@ -107,7 +107,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     headingChanged = (value: string, shiftDown?: boolean) => {
         const castedValue = this.getValue(value);
         if (castedValue) {
-            if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
+            if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
                 return false;
             }
             this.props.docList.forEach(d => (d[this.props.pivotField] = castedValue));
@@ -134,7 +134,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
         const key = this.props.pivotField;
-        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true });
+        const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
         newDoc[key] = this.getValue(this.props.heading);
         const maxHeading = this.props.docList.reduce((maxHeading, doc) => (NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading), 0);
         const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
@@ -147,9 +147,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
     @action
     deleteColumn = () => {
         this.props.docList.forEach(d => (d[this.props.pivotField] = undefined));
-        if (this.props.columnHeaders && this.props.headingObject) {
-            const index = this.props.columnHeaders.indexOf(this.props.headingObject);
-            this.props.columnHeaders.splice(index, 1);
+        if (this.props.colHeaderData && this.props.headingObject) {
+            const index = this.props.colHeaderData.indexOf(this.props.headingObject);
+            this.props.colHeaderData.splice(index, 1);
         }
     };
 
@@ -163,15 +163,15 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     //TODO: I think this is where I'm supposed to edit stuff
     startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
-        // is MakeAlias a way to make a copy of a doc without rendering it?
-        const alias = Doc.MakeAlias(this.props.Document);
-        alias._width = this.props.columnWidth / (this.props.columnHeaders?.length || 1);
-        alias._pivotField = undefined;
+        // is MakeEmbedding a way to make a copy of a doc without rendering it?
+        const embedding = Doc.MakeEmbedding(this.props.Document);
+        embedding._width = this.props.columnWidth / (this.props.colHeaderData?.length || 1);
+        embedding._pivotField = undefined;
         let value = this.getValue(this._heading);
         value = typeof value === 'string' ? `"${value}"` : value;
-        alias.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name });
-        if (alias.viewSpecScript) {
-            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
+        embedding.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name });
+        if (embedding.viewSpecScript) {
+            DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
             return true;
         }
         return false;
@@ -266,7 +266,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true });
+            const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
             if (created) {
                 if (this.props.Document.isTemplateDoc) {
                     Doc.MakeMetadataFieldTemplate(created, this.props.Document);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5b9453666..008de1944 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -129,7 +129,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                     notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, this.props.Document).length > 0;
                     const fieldKey = Doc.LayoutFieldKey(d);
                     const annos = !Field.toString(Doc.LayoutField(d) as Field).includes(CollectionView.name);
-                    const data = d[annos ? fieldKey + '-annotations' : fieldKey];
+                    const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                     if (data !== undefined) {
                         let subDocs = DocListCast(data);
                         if (subDocs.length > 0) {
@@ -141,7 +141,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                     const fieldKey = Doc.LayoutFieldKey(t);
                                     const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name);
                                     notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, d).length));
-                                    DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
+                                    DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                                 });
                                 subDocs = newarray;
                             }
@@ -188,9 +188,9 @@ export function CollectionSubView<X>(moreProps?: X) {
 
         protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
             if (de.complete.docDragData) {
-                // if targetDropAction is, say 'alias', but we're just dragging within a collection, we want to ignore the targetAction.
+                // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
                 // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys)
-                if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.context === this.props.Document && this.childDocs.includes(d))) {
+                if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
                     de.complete.docDragData.dropAction = targetAction;
                 }
                 e.stopPropagation();
@@ -282,7 +282,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                             addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
                         }
                     } else if (text) {
-                        addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: StrCast(Doc.UserDoc().showTitle), _width: 100, _height: 25 }));
+                        addDocument(Docs.Create.TextDocument(text, { ...options, _layout_showTitle: StrCast(Doc.UserDoc().layout_showTitle), _width: 100, _height: 25 }));
                     }
                     return;
                 }
@@ -375,7 +375,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                         _width: 400,
                         _height: 512,
                         _nativeWidth: 850,
-                        useCors: true,
+                        data_useCors: true,
                     })
                 );
                 return;
@@ -417,7 +417,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                                         proto._height = 20;
                                         return proto;
                                     }),
-                                    { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _singleLine: true }
+                                    { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _createDocOnCR: true }
                                 )
                             );
                         });
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 3cdb460a3..6135154c3 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -35,7 +35,7 @@ export class CollectionTimeView extends CollectionSubView() {
     async componentDidMount() {
         this.props.setContentView?.(this);
         //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), "");
-        ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; useRightSplit(alias, shiftKey); ";
+        ///const childText = "const embedding = getEmbedding(self); switchView(embedding, detailView); embedding.dropAction='embed'; useRightSplit(embedding, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
@@ -55,10 +55,10 @@ export class CollectionTimeView extends CollectionSubView() {
 
         if (addAsAnnotation) {
             // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
-                Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+            if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
+                Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
             } else {
-                this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+                this.dataDoc[this.props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
             }
         }
         return anchor;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f774781ab..095e34c39 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -104,7 +104,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     componentDidMount() {
         //this.props.setContentView?.(this);
         this._disposers.autoheight = reaction(
-            () => this.rootDoc.autoHeight,
+            () => this.rootDoc.layout_autoHeight,
             auto => auto && this.computeHeight(),
             { fireImmediately: true }
         );
@@ -114,25 +114,25 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         if (!this._isDisposing) {
             const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
             const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()) + 6;
-            this.layoutDoc._autoHeightMargins = bodyHeight;
+            this.layoutDoc._layout_autoHeightMargins = bodyHeight;
             !this.props.dontRegisterView && this.props.setHeight?.(bodyHeight + titleHeight);
         }
     };
     unobserveHeight = (ref: any) => {
         this.refList.delete(ref);
-        this.rootDoc.autoHeight && this.computeHeight();
+        this.rootDoc.layout_autoHeight && this.computeHeight();
     };
     observeHeight = (ref: any) => {
         if (ref) {
             this.refList.add(ref);
             this.observer = new _global.ResizeObserver(
                 action((entries: any) => {
-                    if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+                    if (this.rootDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
                         this.computeHeight();
                     }
                 })
             );
-            this.rootDoc.autoHeight && this.computeHeight();
+            this.rootDoc.layout_autoHeight && this.computeHeight();
             this.observer.observe(ref);
         }
     };
@@ -144,7 +144,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
         if (dragData) {
-            const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
+            const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d));
             dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction;
         }
     };
@@ -176,7 +176,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const doAddDoc = (doc: Doc | Doc[]) =>
             (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => {
                 const res = flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before);
-                res && (doc.context = this.props.Document);
+                res && (doc.embedContainer = this.props.Document);
                 return res;
             }, true);
         if (this.doc.resolvedDataDoc instanceof Promise) return false;
@@ -234,7 +234,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 isContentActive={this.isContentActive}
                 isDocumentActive={this.isContentActive}
                 rootSelected={returnTrue}
-                forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
+                forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not layout_autoHeight
                 PanelWidth={this.documentTitleWidth}
                 PanelHeight={this.documentTitleHeight}
                 NativeDimScaling={returnOne}
@@ -369,15 +369,15 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     marginTop = () => NumCast(this.doc._yMargin);
     marginBot = () => NumCast(this.doc._yMargin);
     documentTitleWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.panelWidth());
-    documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins);
+    documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.layout_autoHeightMargins);
     truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
     onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
     panelWidth = () => Math.max(0, this.props.PanelWidth() - 2 * this.marginX() * (this.props.NativeDimScaling?.() || 1));
 
-    addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
-    remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
+    addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}_annotations`) || false;
+    remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}_annotations`) || false;
     moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
-        this.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false;
+        this.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}_annotations`) || false;
 
     @observable _headerHeight = 0;
     @computed get content() {
@@ -430,7 +430,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     render() {
         TraceMobx();
 
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1) || 1;
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
         return (
             <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
                 {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
@@ -443,7 +443,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                         isAnnotationOverlay={true}
                         isAnnotationOverlayScrollable={true}
                         childDocumentsActive={this.props.isDocumentActive}
-                        fieldKey={this.props.fieldKey + '-annotations'}
+                        fieldKey={this.props.fieldKey + '_annotations'}
                         dropAction={'move'}
                         select={emptyFunction}
                         addDocument={this.addAnnotationDocument}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b76033a0c..dd7bf1740 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -47,8 +47,8 @@ interface CollectionViewProps_ extends FieldViewProps {
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childContentsActive?: () => boolean | undefined;
-    childFitWidth?: (child: Doc) => boolean;
-    childShowTitle?: () => string;
+    childLayoutFitWidth?: (child: Doc) => boolean;
+    childlayout_showTitle?: () => string;
     childOpacity?: () => number;
     childContextMenuItems?: () => { script: ScriptField; label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
@@ -134,8 +134,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 { description: 'Freeform',    event: () => func(CollectionViewType.Freeform), icon: 'signature' },
                 { description: 'Schema',      event: () => func(CollectionViewType.Schema), icon: 'th-list' },
                 { description: 'Tree',        event: () => func(CollectionViewType.Tree), icon: 'tree' },
-                { description: 'Stacking',    event: () => (func(CollectionViewType.Stacking)._autoHeight = true), icon: 'ellipsis-v' },
-                { description: 'Notetaking',  event: () => (func(CollectionViewType.NoteTaking)._autoHeight = true), icon: 'ellipsis-v' },
+                { description: 'Stacking',    event: () => (func(CollectionViewType.Stacking)._layout_autoHeight = true), icon: 'ellipsis-v' },
+                { description: 'Notetaking',  event: () => (func(CollectionViewType.NoteTaking)._layout_autoHeight = true), icon: 'ellipsis-v' },
                 { description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' },
                 { description: 'Multirow',    event: () => func(CollectionViewType.Multirow), icon: 'columns' },
                 { description: 'Masonry',     event: () => func(CollectionViewType.Masonry), icon: 'columns' },
@@ -159,7 +159,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             !Doc.noviceMode &&
                 this.setupViewTypes('UI Controls...', vtype => {
-                    const newRendition = Doc.MakeAlias(this.rootDoc);
+                    const newRendition = Doc.MakeEmbedding(this.rootDoc);
                     newRendition._viewType = vtype;
                     this.props.addDocTab(newRendition, OpenWhere.addRight);
                     return newRendition;
@@ -190,9 +190,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                         description: `Edit ${func.name} script`,
                         icon: 'edit',
                         event: (obj: any) => {
-                            const alias = Doc.MakeAlias(this.rootDoc);
-                            DocUtils.makeCustomViewClicked(alias, undefined, func.key);
-                            this.props.addDocTab(alias, OpenWhere.addRight);
+                            const embedding = Doc.MakeEmbedding(this.rootDoc);
+                            DocUtils.makeCustomViewClicked(embedding, undefined, func.key);
+                            this.props.addDocTab(embedding, OpenWhere.addRight);
                         },
                     })
                 );
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 45604c1bf..69963736b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -252,11 +252,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const pinDoc = Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
             pinDoc.presentationTargetDoc = anchorDoc ?? doc;
             pinDoc.title = doc.title + ' - Slide';
-            pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
+            pinDoc.data = new List<Doc>(); // the children of the embedding's layout are the presentation slide children.  the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
             pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
             pinDoc.presDuration = pinDoc.presDuration ?? 1000;
             pinDoc.groupWithUp = false;
-            pinDoc.context = curPres;
+            pinDoc.embedContainer = curPres;
             // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
             pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
             pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
@@ -264,7 +264,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc.treeViewFieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
             pinDoc.treeViewExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
             pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
-            const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+            const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null);
 
             if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
             if (!pinProps?.audioRange && duration !== undefined) {
@@ -283,7 +283,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.title = doc.title + ' (move)';
                 pinDoc.presMovement = PresMovement.Pan;
             }
-            if (pinDoc.isInkMask) {
+            if (pinDoc.stroke_isInkMask) {
                 pinDoc.presHideAfter = true;
                 pinDoc.presHideBefore = true;
                 pinDoc.presMovement = PresMovement.None;
@@ -357,7 +357,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         switch (whereFields[0]) {
             case undefined:
             case OpenWhere.lightbox:   if (this.layoutDoc?._isLightbox) {
-                                            const lightboxView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
+                                            const lightboxView = !doc.annotationOn && DocCast(doc.embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.embedContainer)) : undefined;
                                             const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)];
                                             if (lightboxView && (!data || data instanceof List)) {
                                                 lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List<Doc>([doc]);
@@ -409,7 +409,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     miniMapColor = () => this.tabColor;
     tabView = () => this._view;
     disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform;
-    hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
+    hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.layout_hideMinimap);
 
     @computed get docView() {
         return !this._activated || !this._document ? null : (
@@ -449,19 +449,19 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     pinToPres={TabDocView.PinDoc}
                 />
                 <TabMinimapView key="minimap" hideMinimap={this.hideMinimap} addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
-                <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
+                <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
                     <div
                         className="miniMap-hidden"
                         style={{
                             display: this.disableMinimap() || this._document._viewType !== 'freeform' ? 'none' : undefined,
-                            color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE,
-                            backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
-                            boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
+                            color: this._document.layout_hideMinimap ? Colors.BLACK : Colors.WHITE,
+                            backgroundColor: this._document.layout_hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
+                            boxShadow: this._document.layout_hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
                         }}
                         onPointerDown={e => e.stopPropagation()}
                         onClick={action(e => {
                             e.stopPropagation();
-                            this._document!.hideMinimap = !this._document!.hideMinimap;
+                            this._document!.layout_hideMinimap = !this._document!.layout_hideMinimap;
                         })}>
                         <FontAwesomeIcon icon={'globe-asia'} size="lg" />
                     </div>
@@ -559,8 +559,8 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                 this,
                 e,
                 action((e: PointerEvent, down: number[], delta: number[]) => {
-                    doc._panX = clamp(NumCast(doc._panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
-                    doc._panY = clamp(NumCast(doc._panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
+                    doc._freeform_panX = clamp(NumCast(doc._freeform_panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
+                    doc._freeform_panY = clamp(NumCast(doc._freeform_panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
                     return false;
                 }),
                 emptyFunction,
@@ -569,10 +569,10 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
     };
     render() {
         if (!this.renderBounds) return null;
-        const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100;
-        const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100;
-        const miniLeft = 50 + ((NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2;
-        const miniTop = 50 + ((NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
+        const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100;
+        const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100;
+        const miniLeft = 50 + ((NumCast(this.props.document._freeform_) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2;
+        const miniTop = 50 + ((NumCast(this.props.document._freeform_panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
         const miniSize = this.returnMiniSize();
         return this.props.hideMinimap() ? null : (
             <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 4adf86683..92b70cb5a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -102,7 +102,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _treedropDisposer?: DragManager.DragDropDisposer;
 
     get treeViewOpenIsTransient() {
-        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc);
+        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDocDataProto(this.doc);
     }
     set treeViewOpen(c: boolean) {
         if (this.treeViewOpenIsTransient) this._transientOpenState = c;
@@ -125,7 +125,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             : this.props.treeView.fileSysMode
             ? this.doc.isFolder
                 ? this.fieldKey
-                : 'aliases' // for displaying
+                : 'embeddings' // for displaying
             : this.props.treeView.outlineMode || this.childDocs
             ? this.fieldKey
             : Doc.noviceMode
@@ -160,11 +160,11 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get childLinks() {
         return this.childDocList('links');
     }
-    @computed get childAliases() {
-        return this.childDocList('aliases');
+    @computed get childEmbeddings() {
+        return this.childDocList('proto_embeddings');
     }
     @computed get childAnnos() {
-        return this.childDocList(this.fieldKey + '-annotations');
+        return this.childDocList(this.fieldKey + '_annotations');
     }
     @computed get selected() {
         return SelectionManager.IsSelected(this._docRef);
@@ -219,11 +219,13 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
-            // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            const bestAlias =
-                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
-            const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail);
-            this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), OpenWhere.lightbox);
+            // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns,  (2) has no context field ... otherwise make a new embedding
+            const bestEmbedding =
+                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDocDataProto(docView.props.Document)
+                    ? docView.props.Document
+                    : DocListCast(this.props.document.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail);
+            const nextBestEmbedding = DocListCast(this.props.document.proto_embeddings).find(doc => doc.author === Doc.CurrentUserEmail);
+            this.props.addDocTab(bestEmbedding ?? nextBestEmbedding ?? Doc.MakeEmbedding(this.props.document), OpenWhere.lightbox);
         }
     };
 
@@ -311,16 +313,16 @@ export class TreeView extends React.Component<TreeViewProps> {
             treeViewExpandedViewLock: true,
             treeViewExpandedView: 'data',
             _viewType: CollectionViewType.Tree,
-            hideLinkButton: true,
-            _showSidebar: true,
-            _fitWidth: true,
+            layout_hideLinkButton: true,
+            _layout_showSidebar: true,
+            _layout_fitWidth: true,
             treeViewType: TreeViewType.outline,
             x: 0,
             y: 0,
             _xMargin: 0,
             _yMargin: 0,
-            _autoHeight: true,
-            _singleLine: true,
+            _layout_autoHeight: true,
+            _createDocOnCR: true,
             _width: 1000,
             _height: 10,
         });
@@ -359,7 +361,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (de.complete.linkDragData) {
             const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
             const destDoc = this.doc;
-            DocUtils.MakeLink(sourceDoc, destDoc, { linkRelationship: 'tree link' });
+            DocUtils.MakeLink(sourceDoc, destDoc, { link_relationship: 'tree link' });
             e.stopPropagation();
         }
         const docDragData = de.complete.docDragData;
@@ -378,7 +380,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const innerAdd = (doc: Doc) => {
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
-                dataIsComputed && (doc.context = this.doc.context);
+                dataIsComputed && (doc.embedContainer = this.doc.embedContainer);
                 return added;
             };
             return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -413,7 +415,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             (() => {
                 const aspect = Doc.NativeAspect(layoutDoc);
                 if (aspect) return this.embeddedPanelWidth() / (aspect || 1);
-                return layoutDoc._fitWidth
+                return layoutDoc._layout_fitWidth
                     ? !Doc.NativeHeight(layoutDoc)
                         ? NumCast(layoutDoc._height)
                         : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.treeViewParent._height)))
@@ -441,7 +443,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     const innerAdd = (doc: Doc) => {
                         const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                         const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                        dataIsComputed && (doc.context = this.doc.context);
+                        dataIsComputed && (doc.embedContainer = this.doc.embedContainer);
                         return added;
                     };
                     return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -527,7 +529,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const expandKey = this.treeViewExpandedView;
         const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } }) ?? {};
-        if (['links', 'annotations', 'aliases', this.fieldKey].includes(expandKey)) {
+        if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) {
             const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
             const sortKeys = Object.keys(sortings);
             const curSortIndex = Math.max(
@@ -549,12 +551,12 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                !dataIsComputed && added && (doc.context = this.doc.context);
+                !dataIsComputed && added && (doc.embedContainer = this.doc.embedContainer);
 
                 return added;
             };
             const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-            const docs = expandKey === 'aliases' ? this.childAliases : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
+            const docs = expandKey === 'embeddings' ? this.childEmbeddings : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
             let downX = 0,
                 downY = 0;
             if (docs?.length && this._renderCount < docs?.length) {
@@ -706,13 +708,13 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     @computed get validExpandViewTypes() {
-        const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
+        const annos = () => (DocListCast(this.doc[this.fieldKey + '_annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
         const links = () => (LinkManager.Links(this.doc).length && !this.props.treeView.dashboardMode ? 'links' : '');
         const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : '');
-        const aliases = () => (this.props.treeView.dashboardMode ? '' : 'aliases');
+        const embeddings = () => (this.props.treeView.dashboardMode ? '' : 'embeddings');
         const fields = () => (Doc.noviceMode ? '' : 'fields');
         const layout = Doc.noviceMode || this.doc.viewType === CollectionViewType.Docking ? [] : ['layout'];
-        return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
+        return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [embeddings(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
     expandNextviewType = () => {
@@ -759,7 +761,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
         const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' };
         const folderOp = this.childDocs?.length ? [makeFolder] : [];
-        const openAlias = { script: ScriptField.MakeFunction(`openDoc(getAlias(self), ${OpenWhere.addRight})`)!, icon: 'copy', label: 'Open Alias' };
+        const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(self), ${OpenWhere.addRight})`)!, icon: 'copy', label: 'Open Embedding' };
         const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
         return [
             ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
@@ -768,10 +770,10 @@ export class TreeView extends React.Component<TreeViewProps> {
                 : Doc.IsSystem(this.doc)
                 ? []
                 : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
-                ? [openAlias, makeFolder]
+                ? [openEmbedding, makeFolder]
                 : this.doc.viewType === CollectionViewType.Docking
                 ? []
-                : [deleteItem, openAlias, focusDoc]),
+                : [deleteItem, openEmbedding, focusDoc]),
         ];
     };
     childContextMenuItems = () => {
@@ -896,7 +898,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     }
                 })}
                 Document={this.doc}
-                fitWidth={returnTrue}
+                layout_fitWidth={returnTrue}
                 DataDoc={undefined}
                 scriptContext={this}
                 hideDecorationTitle={this.props.treeView.outlineMode}
@@ -985,7 +987,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     ref={action((r: DocumentView | null) => (this._dref = r))}
                     Document={this.doc}
                     DataDoc={undefined}
-                    fitWidth={this.fitWidthFilter}
+                    layout_fitWidth={this.fitWidthFilter}
                     PanelWidth={this.embeddedPanelWidth}
                     PanelHeight={this.embeddedPanelHeight}
                     LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
@@ -1179,7 +1181,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
                         Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
                         newParent.treeViewOpen = true;
-                        child.context = treeView.Document;
+                        child.embedContainer = treeView.Document;
                     }
                 };
                 const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 0dfd119d7..68ba3d368 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -34,11 +34,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         this._anchorDisposer = reaction(
             () => [
                 this.props.A.props.ScreenToLocalTransform(),
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
                 this.props.B.props.ScreenToLocalTransform(),
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[CssSym],
             ],
             action(() => {
                 this._start = Date.now();
@@ -58,7 +58,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             0
         ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
         setTimeout(
-            action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)),
+            action(() => (!LinkDocs.length || !linkDoc.layout_linkDisplay) && (this._opacity = 0.05)),
             750
         ); // this will unhighlight the link line.
         const a = A.ContentDiv.getBoundingClientRect();
@@ -72,36 +72,36 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         //   if there's an element in the DOM with a classname containing a link anchor's id,
         //   then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
         //   otherwise, we just use the computed nearest point on the document boundary to the target Document
-        const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement();
-        const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor2 as Doc)[Id])).lastElement();
+        const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.link_anchor_1 as Doc)[Id])).lastElement();
+        const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.link_anchor_2 as Doc)[Id])).lastElement();
         if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
         if (!targetAhyperlink) {
-            if (linkDoc.linkAutoMove) {
-                linkDoc.anchor1_x = ((apt.point.x - aleft) / awidth) * 100;
-                linkDoc.anchor1_y = ((apt.point.y - atop) / aheight) * 100;
+            if (linkDoc.layout_autoMoveAnchors) {
+                linkDoc.link_anchor_1_x = ((apt.point.x - aleft) / awidth) * 100;
+                linkDoc.link_anchor_1_y = ((apt.point.y - atop) / aheight) * 100;
             }
         } else {
             const m = targetAhyperlink.getBoundingClientRect();
             const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
             const mpx = mp[0] / A.props.PanelWidth();
             const mpy = mp[1] / A.props.PanelHeight();
-            if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
-            if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
+            if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_1_x = mpx * 100;
+            if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_1_y = mpy * 100;
             if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
         if (!targetBhyperlink) {
-            if (linkDoc.linkAutoMove) {
-                linkDoc.anchor2_x = ((bpt.point.x - bleft) / bwidth) * 100;
-                linkDoc.anchor2_y = ((bpt.point.y - btop) / bheight) * 100;
+            if (linkDoc.layout_autoMoveAnchors) {
+                linkDoc.link_anchor_2_x = ((bpt.point.x - bleft) / bwidth) * 100;
+                linkDoc.link_anchor_2_y = ((bpt.point.y - btop) / bheight) * 100;
             }
         } else {
             const m = targetBhyperlink.getBoundingClientRect();
             const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
             const mpx = mp[0] / B.props.PanelWidth();
             const mpy = mp[1] / B.props.PanelHeight();
-            if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
-            if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
+            if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_2_x = mpx * 100;
+            if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_2_y = mpy * 100;
             if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
@@ -112,8 +112,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             this,
             e,
             (e, down, delta) => {
-                this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
-                this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
+                this.props.LinkDocs[0].link_relationship_OffsetX = NumCast(this.props.LinkDocs[0].link_relationship_OffsetX) + delta[0];
+                this.props.LinkDocs[0].link_relationship_OffsetY = NumCast(this.props.LinkDocs[0].link_relationship_OffsetY) + delta[1];
                 return false;
             },
             emptyFunction,
@@ -205,8 +205,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const bclipped = bleft !== b.left || btop !== b.top;
         if (aclipped && bclipped) return undefined;
         const clipped = aclipped || bclipped;
-        const pt1inside = NumCast(LinkDocs[0].anchor1_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor1_y) % 100 !== 0;
-        const pt2inside = NumCast(LinkDocs[0].anchor2_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor2_y) % 100 !== 0;
+        const pt1inside = NumCast(LinkDocs[0].link_anchor_1_x) % 100 !== 0 && NumCast(LinkDocs[0].link_anchor_1_y) % 100 !== 0;
+        const pt2inside = NumCast(LinkDocs[0].link_anchor_2_x) % 100 !== 0 && NumCast(LinkDocs[0].link_anchor_2_y) % 100 !== 0;
         const pt1 = [aleft + a.width / 2, atop + a.height / 2];
         const pt2 = [bleft + b.width / 2, btop + b.width / 2];
         const pt2vec = pt2inside ? [-0.7071, 0.7071] : [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]];
@@ -223,8 +223,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const aActive = A.isSelected() || Doc.IsBrushed(A.rootDoc);
         const bActive = B.isSelected() || Doc.IsBrushed(B.rootDoc);
 
-        const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].linkOffsetX);
-        const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].linkOffsetY);
+        const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
+        const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
         return {
             a,
             b,
@@ -244,12 +244,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
 
         const link = this.props.LinkDocs[0];
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
-        const linkRelationship = Field.toString(link?.linkRelationship as any as Field); //get string representing relationship
-        const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
+        const linkRelationship = Field.toString(link?.link_relationship as any as Field); //get string representing relationship
+        const linkRelationshipList = Doc.UserDoc().link_relationshipList as List<string>;
         const linkColorList = Doc.UserDoc().linkColorList as List<string>;
-        const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List<number>;
+        const linkRelationshipSizes = Doc.UserDoc().link_relationshipSizes as List<number>;
         const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship);
-        const linkDescription = Field.toString(link.description as any as Field);
+        const linkDescription = Field.toString(link.link_description as any as Field);
 
         const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
 
@@ -261,11 +261,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         //thickness varies linearly from 3px to 12px for increasing link count
         const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
 
-        if (link.linkDisplayArrow === undefined) {
-            link.linkDisplayArrow = false;
+        if (link.layout_linkDisplayArrow === undefined) {
+            link.layout_linkDisplayArrow = false;
         }
 
-        return link.opacity === 0 || !a.width || !b.width || (!link.linkDisplay && !aActive && !bActive) ? null : (
+        return link.opacity === 0 || !a.width || !b.width || (!link.layout_linkDisplay && !aActive && !bActive) ? null : (
             <>
                 <defs>
                     <marker id={`${link[Id] + 'arrowhead'}`} markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
@@ -294,7 +294,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                     style={{ pointerEvents: 'visibleStroke', opacity: this._opacity, stroke, strokeWidth }}
                     onClick={this.onClickLine}
                     d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
-                    markerEnd={link.linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
+                    markerEnd={link.layout_linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
                 />
                 {textX === undefined || !linkDescription ? null : (
                     <text filter={`url(#${link[Id] + 'background'})`} className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 08e3174d1..4d18ebeea 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -100,13 +100,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.props.isAnnotationOverlay;
     }
     public get scaleFieldKey() {
-        return this.props.viewField ? this.props.viewField + '-viewScale' : '_viewScale';
+        return (this.props.viewField ?? '') + '_freeform_scale';
     }
     private get panXFieldKey() {
-        return this.props.viewField ? this.props.viewField + '-panX' : '_panX';
+        return (this.props.viewField ?? '') + '_freeform_panX';
     }
     private get panYFieldKey() {
-        return this.props.viewField ? this.props.viewField + '-panY' : '_panY';
+        return (this.props.viewField ?? '') + '_freeform_panY';
     }
     private get borderWidth() {
         return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
@@ -146,7 +146,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         };
     }
     @computed get fitContentsToBox() {
-        return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay;
+        return (this.props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
     }
     @computed get contentBounds() {
         const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
@@ -173,7 +173,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const dv = this.props.DocumentView?.();
         const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         // if freeform has a native aspect, then the panel height needs to be adjusted to match it
-        const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
+        const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.layout_fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
         return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedGetLocalTransform(): Transform {
@@ -239,16 +239,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     shrinkWrap = () => {
         if (this.props.DocumentView?.().nativeWidth) return;
         const vals = this.fitToContentVals;
-        this.layoutDoc._panX = vals.bounds.cx;
-        this.layoutDoc._panY = vals.bounds.cy;
-        this.layoutDoc._viewScale = vals.scale;
+        this.layoutDoc._freeform_panX = vals.bounds.cx;
+        this.layoutDoc._freeform_panY = vals.bounds.cy;
+        this.layoutDoc._freeform_scale = vals.scale;
     };
     freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined);
     reverseNativeScaling = () => (this.fitContentsToBox ? true : false);
-    // panx, pany, zoomscale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
+    // freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
     // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
-    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1));
-    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1));
+    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
+    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
     zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
     contentTransform = () =>
         this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
@@ -297,7 +297,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     isCurrent(doc: Doc) {
         const dispTime = NumCast(doc._timecodeToShow, -1);
         const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
-        const curTime = NumCast(this.Document._currentTimecode, -1);
+        const curTime = NumCast(this.Document._layout_currentTimecode, -1);
         return dispTime === -1 || curTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
     }
 
@@ -348,7 +348,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             .map(pair => pair.layout)
             .slice()
             .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
-        zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1));
+        zsorted.forEach((doc, index) => (doc.zIndex = doc.stroke_isInkMask ? 5000 : index + 1));
         const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
         const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
 
@@ -366,7 +366,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 d.x = x + NumCast(d.x) - dropPos[0];
                 d.y = y + NumCast(d.y) - dropPos[1];
             }
-            d._lastModified = new DateField();
+            d._layout_modificationDate = new DateField();
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? (nd[1] / nd[0]) * NumCast(layoutDoc._width) : 300);
@@ -418,11 +418,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
         if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
             // dragged document is a child of this collection
-            if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) {
-                // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
+            if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.embedContainer !== this.props.Document) {
+                // if the source doc view's embedContainer isn't this same freeformcollectionlinkDragData.dragDocument.embedContainer === this.props.Document
                 const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' });
                 this.props.addDocument?.(source);
-                de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { linkRelationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
+                de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
             }
             e.stopPropagation(); //  do nothing if link is dropped into any freeform view parent of dragged document
             return true;
@@ -446,7 +446,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.childLayoutPairs
             .map(pair => pair.layout)
             .reduce((cluster, cd) => {
-                const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
+                const grouping = this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
                 if (grouping !== -1) {
                     const layoutDoc = Doc.Layout(cd);
                     const cx = NumCast(cd.x) - this._clusterDistance / 2;
@@ -463,10 +463,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (cluster !== -1) {
             const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
             if (ptsParent) {
-                const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster);
+                const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
                 const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!);
                 const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
-                const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'alias' : undefined);
+                const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined);
                 de.moveDocument = this.props.moveDocument;
                 de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
                 DragManager.StartDocumentDrag(
@@ -485,8 +485,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @undoBatch
     @action
-    updateClusters(_useClusters: boolean) {
-        this.props.Document._useClusters = _useClusters;
+    updateClusters(_freeform_useClusters: boolean) {
+        this.props.Document._freeform_useClusters = _freeform_useClusters;
         this._clusterSets.length = 0;
         this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
     }
@@ -494,38 +494,43 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     updateClusterDocs(docs: Doc[]) {
         const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
-        if (this.props.Document._useClusters) {
+        if (this.props.Document._freeform_useClusters) {
             const docFirst = docs[0];
             docs.map(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
-            const preferredInd = NumCast(docFirst.cluster);
-            docs.map(doc => (doc.cluster = -1));
+            const preferredInd = NumCast(docFirst.layout_cluster);
+            docs.map(doc => (doc.layout_cluster = -1));
             docs.map(doc =>
                 this._clusterSets.map((set, i) =>
                     set.map(member => {
-                        if (docFirst.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
-                            docFirst.cluster = i;
+                        if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+                            docFirst.layout_cluster = i;
                         }
                     })
                 )
             );
-            if (docFirst.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
-                docFirst.cluster = preferredInd;
+            if (
+                docFirst.layout_cluster === -1 &&
+                preferredInd !== -1 &&
+                this._clusterSets.length > preferredInd &&
+                (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)
+            ) {
+                docFirst.layout_cluster = preferredInd;
             }
             this._clusterSets.map((set, i) => {
-                if (docFirst.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
-                    docFirst.cluster = i;
+                if (docFirst.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+                    docFirst.layout_cluster = i;
                 }
             });
-            if (docFirst.cluster === -1) {
+            if (docFirst.layout_cluster === -1) {
                 docs.map(doc => {
-                    doc.cluster = this._clusterSets.length;
+                    doc.layout_cluster = this._clusterSets.length;
                     this._clusterSets.push([doc]);
                 });
             } else if (this._clusterSets.length) {
-                for (let i = this._clusterSets.length; i <= NumCast(docFirst.cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
-                docs.map(doc => this._clusterSets[(doc.cluster = NumCast(docFirst.cluster))].push(doc));
+                for (let i = this._clusterSets.length; i <= NumCast(docFirst.layout_cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
+                docs.map(doc => this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc));
             }
-            childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
+            childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child));
         }
     }
 
@@ -533,31 +538,31 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     updateCluster(doc: Doc) {
         const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
-        if (this.props.Document._useClusters) {
+        if (this.props.Document._freeform_useClusters) {
             this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
-            const preferredInd = NumCast(doc.cluster);
-            doc.cluster = -1;
+            const preferredInd = NumCast(doc.layout_cluster);
+            doc.layout_cluster = -1;
             this._clusterSets.forEach((set, i) =>
                 set.forEach(member => {
-                    if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
-                        doc.cluster = i;
+                    if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+                        doc.layout_cluster = i;
                     }
                 })
             );
-            if (doc.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
-                doc.cluster = preferredInd;
+            if (doc.layout_cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
+                doc.layout_cluster = preferredInd;
             }
             this._clusterSets.forEach((set, i) => {
-                if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
-                    doc.cluster = i;
+                if (doc.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+                    doc.layout_cluster = i;
                 }
             });
-            if (doc.cluster === -1) {
-                doc.cluster = this._clusterSets.length;
+            if (doc.layout_cluster === -1) {
+                doc.layout_cluster = this._clusterSets.length;
                 this._clusterSets.push([doc]);
             } else if (this._clusterSets.length) {
-                for (let i = this._clusterSets.length; i <= doc.cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
-                this._clusterSets[doc.cluster ?? 0].push(doc);
+                for (let i = this._clusterSets.length; i <= doc.layout_cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
+                this._clusterSets[doc.layout_cluster ?? 0].push(doc);
             }
         }
     }
@@ -566,8 +571,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props'  used to be renderDepth + 1
         switch (property) {
             case StyleProp.BackgroundColor:
-                const cluster = NumCast(doc?.cluster);
-                if (this.Document._useClusters && doc?.type !== DocumentType.IMG) {
+                const cluster = NumCast(doc?.layout_cluster);
+                if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
                     if (this._clusterSets.length <= cluster) {
                         setTimeout(() => doc && this.updateCluster(doc));
                     } else {
@@ -591,7 +596,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     trySelectCluster = (addToSel: boolean) => {
         if (this._hitCluster !== -1) {
             !addToSel && SelectionManager.DeselectAll();
-            const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+            const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
             this.selectDocuments(eles);
             return true;
         }
@@ -678,7 +683,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(
                     ActiveInkColor(),
-                    Doc.ActiveTool,
                     ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
                     ActiveInkBezierApprox(),
                     ActiveFillColor(),
@@ -834,7 +838,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
             if (!this._deleteList.includes(intersect.inkView)) {
                 this._deleteList.push(intersect.inkView);
-                SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1');
+                SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.stroke_width?.toString()) || '1');
                 SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
                 // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
                 if (!e.shiftKey) {
@@ -1021,19 +1025,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (deltaScale * invTransform.Scale > 20) {
             deltaScale = 20 / invTransform.Scale;
         }
-        if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
+        if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._freeform_scaleMin, 1) && this.isAnnotationOverlay) {
             this.setPan(0, 0);
             return;
         }
-        if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
-            deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale;
+        if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._freeform_scaleMin, 1) && this.isAnnotationOverlay) {
+            deltaScale = NumCast(this.rootDoc._freeform_scaleMin, 1) / invTransform.Scale;
         }
 
         const localTransform = invTransform.scaleAbout(deltaScale, x, y);
         if (localTransform.Scale >= 0.05 || localTransform.Scale > this.zoomScaling()) {
             const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20);
             this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
-            this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
+            this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
         }
     };
 
@@ -1113,26 +1117,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) {
             this.setPanZoomTransition(panTime);
             const scale = this.getLocalTransform().inverse().Scale;
-            const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
-            const minPanX = NumCast(this.rootDoc._panXMin, 0);
-            const minPanY = NumCast(this.rootDoc._panYMin, 0);
-            const newPanX = Math.min(minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
+            const minScale = NumCast(this.rootDoc._freeform_scaleMin, 1);
+            const minPanX = NumCast(this.rootDoc._freeform_panXMin, 0);
+            const minPanY = NumCast(this.rootDoc._freeform_panYMin, 0);
+            const newPanX = Math.min(minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._freeform_panXMax, this.nativeWidth), Math.max(minPanX, panX));
             const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this.props.PanelWidth() - this.props.PanelHeight()) * this.props.ScreenToLocalTransform().Scale) / minScale;
             const nativeHeight = (this.props.PanelHeight() / this.props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
             const maxScrollTop = this.nativeHeight / this.props.ScreenToLocalTransform().Scale - this.props.PanelHeight();
             const maxPanY =
-                minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by fitWidth
+                minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by layout_fitWidth
                 (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, nativeHeight) +
                 (!this.props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
             let newPanY = Math.max(minPanY, Math.min(maxPanY, panY));
-            if (false && NumCast(this.rootDoc.scrollTop) && NumCast(this.rootDoc._viewScale, minScale) !== minScale) {
-                const relTop = NumCast(this.rootDoc.scrollTop) / maxScrollTop;
-                this.rootDoc.scrollTop = undefined;
+            if (false && NumCast(this.rootDoc.layout_scrollTop) && NumCast(this.rootDoc._freeform_scale, minScale) !== minScale) {
+                const relTop = NumCast(this.rootDoc.layout_scrollTop) / maxScrollTop;
+                this.rootDoc.layout_scrollTop = undefined;
                 newPanY = minPanY + relTop * (maxPanY - minPanY);
-            } else if (fitYscroll && this.rootDoc.scrollTop === undefined && NumCast(this.rootDoc._viewScale, minScale) === minScale) {
+            } else if (fitYscroll && this.rootDoc.layout_scrollTop === undefined && NumCast(this.rootDoc._freeform_scale, minScale) === minScale) {
                 const maxPanY = minPanY + fitYscroll;
                 const relTop = (panY - minPanY) / (maxPanY - minPanY);
-                setTimeout(() => (this.rootDoc.scrollTop = relTop * maxScrollTop), 10);
+                setTimeout(() => (this.rootDoc.layout_scrollTop = relTop * maxScrollTop), 10);
                 newPanY = minPanY;
             }
             !this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX);
@@ -1143,7 +1147,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     nudge = (x: number, y: number, nudgeTime: number = 500) => {
         const collectionDoc = this.props.docViewPath().lastElement().rootDoc;
-        if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._panX !== undefined) {
+        if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._freeform_ !== undefined) {
             this.setPan(
                 NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
                 NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(),
@@ -1162,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
             let zfirst = docs.length ? NumCast(docs[0].zIndex) : 0;
             doc.zIndex = zfirst - 1;
-        } else if (doc.isInkMask) {
+        } else if (doc.stroke_isInkMask) {
             doc.zIndex = 5000;
         } else {
             const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
@@ -1222,8 +1226,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const panelWidth = this.props.isAnnotationOverlay ? this.nativeWidth : this.props.PanelWidth();
         const panelHeight = this.props.isAnnotationOverlay ? this.nativeHeight : this.props.PanelHeight();
-        const pw = panelWidth / NumCast(this.layoutDoc._viewScale, 1);
-        const ph = panelHeight / NumCast(this.layoutDoc._viewScale, 1);
+        const pw = panelWidth / NumCast(this.layoutDoc._freeform_scale, 1);
+        const ph = panelHeight / NumCast(this.layoutDoc._freeform_scale, 1);
         const cx = NumCast(this.layoutDoc[this.panXFieldKey]) + (this.props.isAnnotationOverlay ? pw / 2 : 0);
         const cy = NumCast(this.layoutDoc[this.panYFieldKey]) + (this.props.isAnnotationOverlay ? ph / 2 : 0);
         const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
@@ -1248,17 +1252,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._singleLine) && ['Tab', 'Enter'].includes(e.key)) {
+        if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== 'Tab';
-            const layoutKey = StrCast(docView.LayoutFieldKey);
+            const layout_fieldKey = StrCast(docView.LayoutFieldKey);
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
             if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
             else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
-            if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
-                newDoc[layoutKey] = docView.rootDoc[layoutKey];
+            if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
+                newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
@@ -1315,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 sizeProvider={this.childSizeProvider}
                 dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
                 bringToFront={this.bringToFront}
-                showTitle={this.props.childShowTitle}
+                layout_showTitle={this.props.childlayout_showTitle}
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
                 pointerEvents={this.pointerEvents}
                 //rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
@@ -1329,7 +1333,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case OpenWhere.inParent:
                 return this.props.addDocument?.(doc) || false;
             case OpenWhere.inParentFromScreen:
-                const docContext = DocCast((doc instanceof Doc ? doc : doc?.[0])?.context);
+                const docContext = DocCast((doc instanceof Doc ? doc : doc?.[0])?.embedContainer);
                 return (
                     (this.addDocument?.(
                         (doc instanceof Doc ? [doc] : doc).map(doc => {
@@ -1521,7 +1525,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 elements.push({
                     ele: this.getChildDocView(entry[1]),
                     bounds: childData.opacity === 0 ? { ...childData, width: 0, height: 0 } : { ...childData, width: childSize.width, height: childSize.height },
-                    inkMask: BoolCast(entry[1].pair.layout.isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
+                    inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
                 });
             });
 
@@ -1531,7 +1535,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             else this.props.Document[this.scaleFieldKey] = Math.max(1, this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
         }
 
-        this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
+        this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
         return elements;
     }
 
@@ -1541,10 +1545,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document._isGroup, viewType: true, filters: true } }, this.rootDoc);
 
         if (addAsAnnotation) {
-            if (Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), null) !== undefined) {
-                Cast(this.dataDoc[this.props.fieldKey + '-annotations'], listSpec(Doc), []).push(anchor);
+            if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
+                Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
             } else {
-                this.dataDoc[this.props.fieldKey + '-annotations'] = new List<Doc>([anchor]);
+                this.dataDoc[this.props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
             }
         }
         return anchor;
@@ -1644,8 +1648,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             '',
             (iconFile, nativeWidth, nativeHeight) => {
                 this.dataDoc.icon = new ImageField(iconFile);
-                this.dataDoc['icon-nativeWidth'] = nativeWidth;
-                this.dataDoc['icon-nativeHeight'] = nativeHeight;
+                this.dataDoc['icon_nativeWidth'] = nativeWidth;
+                this.dataDoc['icon_nativeHeight'] = nativeHeight;
             }
         );
 
@@ -1762,7 +1766,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         appearanceItems.push({ description: (Doc.UserDoc().defaultToFlashcards ? 'Disable' : 'Enable') + ' Flashcard Notes', event: () => (Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards), icon: 'eye' });
         appearanceItems.push({
             description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`,
-            event: () => (this.Document._fitContentsToBox = !this.fitContentsToBox),
+            event: () => (this.Document._freeform_fitContentsToBox = !this.fitContentsToBox),
             icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt',
         });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
@@ -1781,7 +1785,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         !Doc.noviceMode
             ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? 'Hide' : 'Show') + ' Snap Lines', event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: 'compress-arrows-alt' })
             : null;
-        !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._useClusters), icon: 'braille' }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' });
 
         const options = ContextMenu.Instance.findByDescription('Options...');
@@ -1897,7 +1901,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
                     }}
                     style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
-                    {this.layoutDoc._backgroundGridShow ? (
+                    {this.layoutDoc._freeform_backgroundGrid ? (
                         <div>
                             <CollectionFreeFormBackgroundGrid // bcz : UGHH  don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
                                 PanelWidth={this.props.PanelWidth}
@@ -1937,7 +1941,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
-        return wscale < hscale || this.layoutDoc.fitWidth ? wscale : hscale;
+        return wscale < hscale || this.layoutDoc.layout_fitWidth ? wscale : hscale;
     }
     nativeDim = () => this.nativeDimScaling;
 
@@ -2310,7 +2314,7 @@ ScriptingGlobals.add(function sendToBack(doc: Doc) {
 });
 ScriptingGlobals.add(function resetView() {
     SelectionManager.Docs().forEach(doc => {
-        doc._panX = doc._panY = 0;
-        doc._viewScale = 1;
+        doc._freeform_panX = doc._freeform_panY = 0;
+        doc._freeform_scale = 1;
     });
 });
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 11d466b0f..fdff13b54 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -52,8 +52,8 @@ export interface MarqueeViewBounds {
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
     public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) {
-        const ps = NumCast(pinDoc._viewScale, 1);
-        return { left: NumCast(pinDoc._panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
+        const ps = NumCast(pinDoc._freeform_scale, 1);
+        return { left: NumCast(pinDoc._freeform_panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._freeform_panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
     }
 
     private _commandExecuted = false;
@@ -110,7 +110,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const cm = ContextMenu.Instance;
         const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
         if (e.key === '?') {
-            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), OpenWhere.addRight));
+            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
 
             cm.displayMenu(this._downX, this._downY, undefined, true);
             e.stopPropagation();
@@ -196,7 +196,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             const rowProto = new Doc();
             rowProto.title = rowProto.Id;
             rowProto._width = 200;
-            rowProto.isPrototype = true;
+            rowProto.isDataDoc = true;
             for (let i = 1; i < ns.length - 1; i++) {
                 const values = ns[i].split('\t');
                 if (values.length === 1 && columns.length > 1) {
@@ -204,7 +204,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                     continue;
                 }
                 const docDataProto = Doc.MakeDelegate(rowProto);
-                docDataProto.isPrototype = true;
+                docDataProto.isDataDoc = true;
                 columns.forEach((col, i) => (docDataProto[columns[i]] = values.length > i ? (values[i].indexOf(Number(values[i]).toString()) !== -1 ? Number(values[i]) : values[i]) : undefined));
                 if (groupAttr) {
                     docDataProto._group = groupAttr;
@@ -379,10 +379,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                   Doc.GetProto(doc).data = new List<Doc>(selected);
                   Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
                   !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc));
-                  doc._panX = doc._panY = 0;
+                  doc._freeform_panX = doc._freeform_panY = 0;
                   return doc;
               })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
-        newCollection.system = undefined;
+        newCollection.isSystem = undefined;
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
@@ -390,8 +390,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.enableDragWhenActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
-        newCollection.fitWidth = true;
-        selected.forEach(d => (d.context = newCollection));
+        newCollection.layout_fitWidth = true;
+        selected.forEach(d => (d.embedContainer = newCollection));
         this.hideMarquee();
         return newCollection;
     });
@@ -516,9 +516,19 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, followLinkToggle: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
+        const summary = Docs.Create.TextDocument('', {
+            backgroundColor: '#e2ad32',
+            x: this.Bounds.left,
+            y: this.Bounds.top,
+            followLinkToggle: true,
+            _width: 200,
+            _height: 200,
+            _layoutFitContentsToBox: true,
+            _layout_showSidebar: true,
+            title: 'overview',
+        });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
-        DocUtils.MakeLink(summary, portal, { linkRelationship: 'summary of:summarized by' });
+        DocUtils.MakeLink(summary, portal, { link_relationship: 'summary of:summarized by' });
 
         portal.hidden = true;
         this.props.addDocument?.(portal);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 588affc1c..bcc2ca129 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -79,7 +79,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     @observable _selectedCell: [Doc, number] | undefined;
 
     @computed get _selectedDocs() {
-        return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.rootDoc));
+        return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
     }
 
     @computed get documentKeys() {
@@ -87,7 +87,7 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get previewWidth() {
-        return NumCast(this.layoutDoc.schemaPreviewWidth);
+        return NumCast(this.layoutDoc.schema_previewWidth);
     }
 
     @computed get tableWidth() {
@@ -95,12 +95,12 @@ export class CollectionSchemaView extends CollectionSubView() {
     }
 
     @computed get columnKeys() {
-        return Cast(this.layoutDoc.columnKeys, listSpec('string'), defaultColumnKeys);
+        return Cast(this.layoutDoc.schema_columnKeys, listSpec('string'), defaultColumnKeys);
     }
 
     @computed get storedColumnWidths() {
         const widths = NumListCast(
-            this.layoutDoc.columnWidths,
+            this.layoutDoc.schema_columnWidths,
             this.columnKeys.map(() => (this.tableWidth - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length)
         );
 
@@ -243,7 +243,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
         let currKeys = [...this.columnKeys];
         currKeys[index] = newKey;
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
     };
 
     @undoBatch
@@ -257,11 +257,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         const currWidths = this.storedColumnWidths.slice();
         currWidths.splice(0, 0, newColWidth);
         const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
-        this.layoutDoc.columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
+        this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
 
         let currKeys = this.columnKeys.slice();
         currKeys.splice(0, 0, key);
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
     };
 
     @action
@@ -274,11 +274,11 @@ export class CollectionSchemaView extends CollectionSubView() {
         const currWidths = this.storedColumnWidths.slice();
         currWidths.splice(index, 1);
         const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
-        this.layoutDoc.columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
+        this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
 
         let currKeys = this.columnKeys.slice();
         currKeys.splice(index, 1);
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
     };
 
     @action
@@ -317,7 +317,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
     @action
     finishResize = () => {
-        this.layoutDoc.columnWidths = new List<number>(this._displayColumnWidths);
+        this.layoutDoc.schema_columnWidths = new List<number>(this._displayColumnWidths);
         this._displayColumnWidths = undefined;
     };
 
@@ -326,11 +326,11 @@ export class CollectionSchemaView extends CollectionSubView() {
     moveColumn = (fromIndex: number, toIndex: number) => {
         let currKeys = this.columnKeys.slice();
         currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
-        this.layoutDoc.columnKeys = new List<string>(currKeys);
+        this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
 
         let currWidths = this.storedColumnWidths.slice();
         currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]);
-        this.layoutDoc.columnWidths = new List<number>(currWidths);
+        this.layoutDoc.schema_columnWidths = new List<number>(currWidths);
     };
 
     @action
@@ -480,14 +480,14 @@ export class CollectionSchemaView extends CollectionSubView() {
         const maxWidth = 1000;
         const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
         const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
-        this.layoutDoc.schemaPreviewWidth = width;
+        this.layoutDoc.schema_previewWidth = width;
         return false;
     };
 
     @action
     addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
         if (!value && !forceEmptyNote) return false;
-        const newDoc = Docs.Create.TextDocument(value, { title: value, _autoHeight: true });
+        const newDoc = Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true });
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
         return this.addRow(newDoc) || false;
@@ -500,7 +500,7 @@ export class CollectionSchemaView extends CollectionSubView() {
 
         ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = '';
-            this.addRow(Docs.Create.TextDocument('', { title: name, _autoHeight: true }));
+            this.addRow(Docs.Create.TextDocument('', { title: name, _layout_autoHeight: true }));
         });
         ContextMenu.Instance.displayMenu(x, y, undefined, true);
     };
@@ -837,7 +837,7 @@ export class CollectionSchemaView extends CollectionSubView() {
               });
         return { docs };
     }
-    rowHeightFunc = () => (BoolCast(this.layoutDoc._singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
+    rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
     sortedDocsFunc = () => this.sortedDocs;
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
@@ -968,7 +968,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 hideTitle={true}
                                 hideDocumentButtonBar={true}
                                 hideLinkAnchors={true}
-                                fitWidth={returnTrue}
+                                layout_fitWidth={returnTrue}
                                 scriptContext={this}
                                 canEmbedOnDrag={true}
                             />
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 34631e0b7..45bfe4f77 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -59,7 +59,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onPointerMove = (e: any) => {
         if (!SnappingManager.GetIsDragging()) return;
-        const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.context === this.schemaDoc); //  this.schemaView?._selectedDocs.has(doc) ?? false;
+        const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); //  this.schemaView?._selectedDocs.has(doc) ?? false;
 
         if (this._ref && dragIsRow) {
             const rect = this._ref.getBoundingClientRect();
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 9f659752a..42bf32475 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -73,7 +73,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'alias' as dropActionType,
+            dropAction: 'embed',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
@@ -83,7 +83,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             addDocTab: SchemaTableCell.addFieldDoc,
             pinToPres: returnZero,
             Document,
-            fieldKey,
+            fieldKey: fieldKey,
             PanelWidth: columnWidth,
             PanelHeight: props.rowHeight,
         };
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index d02a1c4eb..b9d56541a 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -24,12 +24,12 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
     private _menuRef = React.createRef<HTMLDivElement>();
 
     getBackgroundColor = (): string => {
-        const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList);
+        const link_relationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
         const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
         let color = 'white';
         // if this link's relationship property is not default "link", set its color
-        if (linkRelationshipList) {
-            const relationshipIndex = linkRelationshipList.indexOf(this.props.groupType);
+        if (link_relationshipList) {
+            const relationshipIndex = link_relationshipList.indexOf(this.props.groupType);
             const RGBcolor: string = linkColorList[relationshipIndex];
             if (RGBcolor) {
                 //set opacity to 0.25 by modifiying the rgb string
@@ -47,17 +47,18 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
             const sourceDoc =
                 this.props.docView.anchorViewDoc ??
                 (this.props.docView.rootDoc.type === DocumentType.LINK //
-                    ? this.props.docView.props.LayoutTemplateString?.includes('anchor1')
-                        ? DocCast(linkDoc.anchor1)
-                        : DocCast(linkDoc.anchor2)
+                    ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
+                        ? DocCast(linkDoc.link_anchor_1)
+                        : DocCast(linkDoc.link_anchor_2)
                     : this.props.sourceDoc);
             const destDoc = !sourceDoc
                 ? undefined
                 : this.props.docView.rootDoc.type === DocumentType.LINK
-                ? this.props.docView.props.LayoutTemplateString?.includes('anchor1')
-                    ? DocCast(linkDoc.anchor2)
-                    : DocCast(linkDoc.anchor1)
-                : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) || LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, Doc, null));
+                ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1')
+                    ? DocCast(linkDoc.link_anchor_2)
+                    : DocCast(linkDoc.link_anchor_1)
+                : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) ||
+                  LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null));
             return !destDoc || !sourceDoc ? null : (
                 <LinkMenuItem
                     key={linkDoc[Id]}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 19d6c2ae2..5af05e491 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -8,15 +8,13 @@ import { Cast, DocCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
-import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SettingsManager } from '../../util/SettingsManager';
 import { undoBatch } from '../../util/UndoManager';
-import { MainView } from '../MainView';
-import { DocumentView, OpenWhere } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
 import React = require('react');
@@ -32,7 +30,7 @@ interface LinkMenuItemProps {
     itemHandler?: (doc: Doc) => void;
 }
 
-// drag links and drop link targets (aliasing them if needed)
+// drag links and drop link targets (embedding them if needed)
 export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) {
     const draggedDocs = (specificLinks ? specificLinks : LinkManager.Links(sourceDoc)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[];
 
@@ -45,7 +43,7 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
 
         const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
         dragData.canEmbed = true;
-        dragData.dropAction = 'alias';
+        dragData.dropAction = 'embed';
 
         DragManager.StartDocumentDrag([dragEle], dragData, downX, downY, undefined);
     }
@@ -65,9 +63,9 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
     @computed get sourceAnchor() {
         const ldoc = this.props.linkDoc;
-        if (this.props.sourceDoc !== ldoc.anchor1 && this.props.sourceDoc !== ldoc.anchor2) {
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor1);
-            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor2);
+        if (this.props.sourceDoc !== ldoc.link_anchor_1 && this.props.sourceDoc !== ldoc.link_anchor_2) {
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_1);
+            if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_2);
         }
         return this.props.sourceDoc;
     }
@@ -79,7 +77,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             this,
             e,
             e => {
-                const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'alias');
+                const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'embed');
                 dragData.removeDropProperties = ['hidden'];
                 DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
                 return true;
@@ -89,7 +87,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                 const trail = DocCast(this.props.docView.rootDoc.presTrail);
                 if (trail) {
                     Doc.ActivePresentation = trail;
-                    MainView.addDocTabFunc(trail, OpenWhere.replaceRight);
+                    DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight);
                 } else {
                     SelectionManager.SelectView(this.props.docView, false);
                     if ((SettingsManager.propertiesWidth ?? 0) < 100) {
@@ -118,10 +116,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                     this.props.itemHandler?.(this.props.linkDoc);
                 } else {
                     const focusDoc =
-                        Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc
-                            ? Cast(this.props.linkDoc.anchor1, Doc, null)
-                            : Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc
-                            ? Cast(this.props.linkDoc.anchor12, Doc, null)
+                        Cast(this.props.linkDoc.link_anchor_1, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.link_anchor_1, Doc, null)
+                            : Cast(this.props.linkDoc.link_anchor_2, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.link_anchor_12, Doc, null)
                             : undefined;
 
                     if (focusDoc) this.props.docView.props.focus(focusDoc, { instant: true });
@@ -149,7 +147,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
         return (
             <div className="linkMenu-item" style={{ background: LinkManager.currentLink === this.props.linkDoc ? 'lightBlue' : undefined }}>
-                <div className={'linkMenu-item-content expand-two'}>
+                <div className="linkMenu-item-content expand-two">
                     <div
                         ref={this._drag}
                         className="linkMenu-name" //title="drag to view target. click to customize."
@@ -190,7 +188,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                                     {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
                                 </p>
                             </div>
-                            {!this.props.linkDoc.description ? null : <p className="linkMenu-description">{StrCast(this.props.linkDoc.description)}</p>}
+                            {!this.props.linkDoc.link_description ? null : <p className="linkMenu-description">{StrCast(this.props.linkDoc.link_description)}</p>}
                         </div>
 
                         <div className="linkMenu-item-buttons">
diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx
index 53da880e4..9662b2fea 100644
--- a/src/client/views/linking/LinkRelationshipSearch.tsx
+++ b/src/client/views/linking/LinkRelationshipSearch.tsx
@@ -1,8 +1,8 @@
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import './LinkEditor.scss';
-import React = require("react");
+import React = require('react');
 
-interface LinkRelationshipSearchProps {
+interface link_relationshipSearchProps {
     results: string[] | undefined;
     display: string;
     //callback fn to set rel + hide dropdown upon setting
@@ -10,48 +10,48 @@ interface LinkRelationshipSearchProps {
     toggleSearch: () => void;
 }
 @observer
-export class LinkRelationshipSearch extends React.Component<LinkRelationshipSearchProps> {
-
+export class link_relationshipSearch extends React.Component<link_relationshipSearchProps> {
     handleResultClick = (e: React.MouseEvent) => {
         const relationship = (e.target as HTMLParagraphElement).textContent;
         if (relationship) {
             this.props.handleRelationshipSearchChange(relationship);
         }
-    }
+    };
 
     handleMouseEnter = () => {
         this.props.toggleSearch();
-    }
+    };
 
     handleMouseLeave = () => {
         this.props.toggleSearch();
-    }
+    };
 
     /**
-     * Render an empty div to increase the height of LinkEditor to accommodate 2+ results 
-    */
+     * Render an empty div to increase the height of LinkEditor to accommodate 2+ results
+     */
     emptyDiv = () => {
-        if (this.props.results && this.props.results.length > 2 && this.props.display === "block") {
-            return <div style={{ height: "50px" }} />;
+        if (this.props.results && this.props.results.length > 2 && this.props.display === 'block') {
+            return <div style={{ height: '50px' }} />;
         }
-    }
+    };
 
     render() {
         return (
             <div className="linkEditor-relationship-dropdown-container">
-                <div className="linkEditor-relationship-dropdown"
-                    style={{ display: this.props.display }}
-                    onMouseEnter={this.handleMouseEnter}
-                    onMouseLeave={this.handleMouseLeave}
-                >
-                    {   // return a dropdown of relationship results if there exist results
-                        this.props.results
-                            ? this.props.results.map(result => {
-                                return <p key={result} onClick={this.handleResultClick}>
-                                    {result}
-                                </p>;
+                <div className="linkEditor-relationship-dropdown" style={{ display: this.props.display }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+                    {
+                        // return a dropdown of relationship results if there exist results
+                        this.props.results ? (
+                            this.props.results.map(result => {
+                                return (
+                                    <p key={result} onClick={this.handleResultClick}>
+                                        {result}
+                                    </p>
+                                );
                             })
-                            : <p>No matching relationships</p>
+                        ) : (
+                            <p>No matching relationships</p>
+                        )
                     }
                 </div>
 
@@ -60,4 +60,4 @@ export class LinkRelationshipSearch extends React.Component<LinkRelationshipSear
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 68fb19208..8e83cf121 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -76,7 +76,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return DateCast(this.dataDoc[this.fieldKey + '-recordingStart'])?.date.getTime();
     }
     @computed get rawDuration() {
-        return NumCast(this.dataDoc[`${this.fieldKey}-duration`]);
+        return NumCast(this.dataDoc[`${this.fieldKey}_duration`]);
     } // bcz: shouldn't be needed since it's computed from audio element
     // mehek: not 100% sure but i think due to the order in which things are loaded this is necessary ^^
     // if you get rid of it and set the value to 0 the timeline and waveform will set their bounds incorrectly
@@ -123,12 +123,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     getLinkData(l: Doc) {
-        let la1 = l.anchor1 as Doc;
-        let la2 = l.anchor2 as Doc;
+        let la1 = l.link_anchor_1 as Doc;
+        let la2 = l.link_anchor_2 as Doc;
         const linkTime = this.timeline?.anchorStart(la2) || this.timeline?.anchorStart(la1) || 0;
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
-            la1 = l.anchor2 as Doc;
-            la2 = l.anchor1 as Doc;
+            la1 = l.link_anchor_2 as Doc;
+            la2 = l.link_anchor_1 as Doc;
         }
         return { la1, la2, linkTime };
     }
@@ -139,7 +139,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.rootDoc,
                 this.dataDoc,
                 this.annotationKey,
-                this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
+                this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
                 undefined,
                 undefined,
                 addAsAnnotation
@@ -155,12 +155,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this.links
                 .map(l => this.getLinkData(l))
                 .forEach(({ la1, la2, linkTime }) => {
-                    if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < this._ele!.currentTime) {
+                    if (linkTime > NumCast(this.layoutDoc._layout_currentTimecode) && linkTime < this._ele!.currentTime) {
                         Doc.linkFollowHighlight(la1);
                     }
                 });
-            this.layoutDoc._currentTimecode = this._ele.currentTime;
-            this.timeline?.scrollToTime(NumCast(this.layoutDoc._currentTimecode));
+            this.layoutDoc._layout_currentTimecode = this._ele.currentTime;
+            this.timeline?.scrollToTime(NumCast(this.layoutDoc._layout_currentTimecode));
         }
     };
 
@@ -221,7 +221,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (this.mediaState === media_state.Recording) {
             setTimeout(this.updateRecordTime, 30);
             if (!this._paused) {
-                this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this._pausedTime) / 1000;
+                this.layoutDoc._layout_currentTimecode = (new Date().getTime() - this._recordStart - this._pausedTime) / 1000;
             }
         }
     };
@@ -253,7 +253,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._recorder = undefined;
             const now = new Date().getTime();
             this._paused && (this._pausedTime += now - this._pauseStart);
-            this.dataDoc[this.fieldKey + '-duration'] = (now - this._recordStart - this._pausedTime) / 1000;
+            this.dataDoc[this.fieldKey + '_duration'] = (now - this._recordStart - this._pausedTime) / 1000;
             this.mediaState = media_state.Paused;
             this._stream?.getAudioTracks()[0].stop();
             const ind = DocUtils.ActiveRecordings.indexOf(this);
@@ -422,11 +422,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
                 this.playFrom(this.timeline?.anchorStart(link) || 0, this.timeline?.anchorEnd(link));
             } else {
-                this._ele!.currentTime = this.layoutDoc._currentTimecode = this.timeline?.anchorStart(link) || 0;
+                this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = this.timeline?.anchorStart(link) || 0;
             }
         } else {
             this.links
-                .filter(l => l.anchor1 === link || l.anchor2 === link)
+                .filter(l => l.link_anchor_1 === link || l.link_anchor_2 === link)
                 .forEach(l => {
                     const { la1, la2 } = this.getLinkData(l);
                     const startTime = this.timeline?.anchorStart(la1) || this.timeline?.anchorStart(la2);
@@ -435,7 +435,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
                             this.playFrom(startTime, endTime);
                         } else {
-                            this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime;
+                            this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = startTime;
                         }
                     }
                 });
@@ -447,7 +447,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     timelineScreenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -AudioBox.topControlsHeight);
 
-    setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._currentTimecode = time);
+    setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = time);
 
     playing = () => this.mediaState === media_state.Playing;
 
@@ -547,7 +547,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
                             <FontAwesomeIcon size="2x" icon={this._paused ? 'play' : 'pause'} />
                         </div>
-                        <div className="record-timecode">{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}</div>
+                        <div className="record-timecode">{formatTime(Math.round(NumCast(this.layoutDoc._layout_currentTimecode)))}</div>
                     </div>
                 ) : (
                     <div className="audiobox-start-record" onPointerDown={this.Record}>
@@ -623,7 +623,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 {this.audio}
 
                 <div className="audiobox-timecodes">
-                    <div className="timecode-current">{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}</div>
+                    <div className="timecode-current">{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._layout_currentTimecode) - NumCast(this.timeline.clipStart)))}</div>
                     {this.miniPlayer ? (
                         <div>/</div>
                     ) : (
@@ -656,6 +656,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 ref={action((r: any) => (this._stackedTimeline = r))}
                 {...this.props}
                 CollectionFreeFormDocumentView={undefined}
+                dataFieldKey={this.fieldKey}
                 fieldKey={this.annotationKey}
                 dictationKey={this.fieldKey + '-dictation'}
                 mediaPath={this.path}
@@ -689,7 +690,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             <audio
                 ref={this.setRef}
                 className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}
-                onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '-duration'] = this._ele.duration))}>
+                onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '_duration'] = this._ele.duration))}>
                 <source src={this.path} type="audio/mpeg" />
                 Not supported.
             </audio>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 862d0da8c..6710cee63 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -36,13 +36,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         { key: 'x' },
         { key: 'y' },
         { key: '_rotation', val: 0 },
-        { key: '_scrollTop' },
+        { key: '_layout_scrollTop' },
         { key: 'opacity', val: 1 },
         { key: '_currentFrame' },
-        { key: 'viewScale', val: 1 },
-        { key: 'viewScale', val: 1 },
-        { key: 'panX' },
-        { key: 'panY' },
+        { key: 'freeform_scale', val: 1 },
+        { key: 'freeform_scale', val: 1 },
+        { key: 'freeform_panX' },
+        { key: 'freeform_panY' },
     ]; // fields that are configured to be animatable using animation frames
     public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
     public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
@@ -154,8 +154,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
             CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (doc[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode)));
             const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so
-            //doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
-            targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0');
+            //doc !== targetDoc && (targetDoc.embedContainer = doc.embedContainer); // the computed fields don't see the layout doc -- need to copy the embedContainer to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!)
+            targetDoc.activeFrame = ComputedField.MakeFunction('self.embedContainer?._currentFrame||0');
             targetDoc.dataTransition = 'inherit';
         });
     }
@@ -202,7 +202,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
         };
-        const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString && !this.layoutDoc._isInkMask;
+        const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask;
         return (
             <div
                 className="collectionFreeFormDocumentView-container"
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 70ba7e182..330cc3971 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -48,7 +48,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     render() {
-        const scaling = Math.min(this.layoutDoc.fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]());
+        const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]());
         return (
             <div
                 className={`colorBox-container${this.props.isContentActive() ? '-interactive' : ''}`}
@@ -72,7 +72,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             SetActiveInkWidth(e.target.value);
                             SelectionManager.Views()
                                 .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK)
-                                .map(i => (i.rootDoc.strokeWidth = Number(e.target.value)));
+                                .map(i => (i.rootDoc.stroke_width = Number(e.target.value)));
                         }}
                     />
                 </div>
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index ace388c57..3be2d9a77 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
@@ -26,6 +26,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     @observable _animating = '';
 
+    @computed get clipWidth() {
+        return NumCast(this.layoutDoc[this.clipWidthKey], 50);
+    }
+    get clipWidthKey() {
+        return '_' + this.props.fieldKey + '_clipWidth';
+    }
+
     componentDidMount() {
         this.props.setContentView?.(this);
     }
@@ -59,7 +66,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                 action(() => {
                     // on click, animate slider movement to the targetWidth
                     this._animating = 'all 200ms';
-                    this.layoutDoc._clipWidth = (targetWidth * 100) / this.props.PanelWidth();
+                    this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth();
                     setTimeout(
                         action(() => (this._animating = '')),
                         200
@@ -71,9 +78,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     @action
     private onPointerMove = ({ movementX }: PointerEvent) => {
-        const width = movementX * this.props.ScreenToLocalTransform().Scale + (NumCast(this.layoutDoc._clipWidth) / 100) * this.props.PanelWidth();
+        const width = movementX * this.props.ScreenToLocalTransform().Scale + (this.clipWidth / 100) * this.props.PanelWidth();
         if (width && width > 5 && width < this.props.PanelWidth()) {
-            this.layoutDoc._clipWidth = (width * 100) / this.props.PanelWidth();
+            this.layoutDoc[this.clipWidthKey] = (width * 100) / this.props.PanelWidth();
         }
         return false;
     };
@@ -101,7 +108,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     };
 
     render() {
-        const clipWidth = NumCast(this.layoutDoc._clipWidth) + '%';
         const clearButton = (which: string) => {
             return (
                 <div
@@ -143,7 +149,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         };
         const displayBox = (which: string, index: number, cover: number) => {
             return (
-                <div className={`${which}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
+                <div className={`${index === 0 ? 'before' : 'after'}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
                     {displayDoc(which)}
                 </div>
             );
@@ -151,16 +157,16 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
         return (
             <div className={`comparisonBox${this.props.isContentActive() || SnappingManager.GetIsDragging() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
-                {displayBox(this.fieldKey === 'data' ? 'compareBox-after' : `${this.fieldKey}2`, 1, this.props.PanelWidth() - 3)}
-                <div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
-                    {displayBox(this.fieldKey === 'data' ? 'compareBox-before' : `${this.fieldKey}1`, 0, 0)}
+                {displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)}
+                <div className="clip-div" style={{ width: this.clipWidth + '%', transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
+                    {displayBox(`${this.fieldKey}_1`, 0, 0)}
                 </div>
 
                 <div
                     className="slide-bar"
                     style={{
-                        left: `calc(${clipWidth} - 0.5px)`,
-                        cursor: NumCast(this.layoutDoc._clipWidth) < 5 ? 'e-resize' : NumCast(this.layoutDoc._clipWidth) / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
+                        left: `calc(${this.clipWidth + '%'} - 0.5px)`,
+                        cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
                     }}
                     onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
                 >
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index eb25d3264..5876efb01 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, StrListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Cast, CsvCast, NumCast, StrCast } from '../../../../fields/Types';
 import { CsvField } from '../../../../fields/URLField';
 import { Docs } from '../../../documents/Documents';
 import { ViewBoxAnnotatableComponent } from '../../DocComponent';
@@ -29,7 +29,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     // @observable private pairs: { [key: string]: FieldResult }[] = [];
     static pairSet = new ObservableMap<string, { [key: string]: string }[]>();
     @computed.struct get pairs() {
-        return DataVizBox.pairSet.get(StrCast(this.rootDoc.fileUpload));
+        return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href);
     }
     private _chartRenderer: LineChart | undefined;
     // // another way would be store a schema that defines the type of data we are expecting from an imported doc
@@ -61,7 +61,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @action
     restoreView = (data: Doc) => {
         const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView);
-        const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._dataVizAxes = new List<string>(StrListCast(data.presDataVizAxes)));
+        const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List<string>(StrListCast(data.presDataVizAxes)));
         const func = () => this._chartRenderer?.restoreView(data);
         if (changedView || changedAxes) {
             setTimeout(func, 100);
@@ -88,14 +88,14 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     };
 
     @computed.struct get axes() {
-        return StrListCast(this.layoutDoc.dataVizAxes);
+        return StrListCast(this.layoutDoc.data_vizAxes);
     }
-    selectAxes = (axes: string[]) => (this.layoutDoc.dataVizAxes = new List<string>(axes));
+    selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List<string>(axes));
 
     @computed get selectView() {
         const width = this.props.PanelWidth() * 0.9;
         const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9;
-        const margin = { top: 10, right: 25, bottom: 50, left:25};
+        const margin = { top: 10, right: 25, bottom: 50, left: 25 };
         if (!this.pairs) return 'no data';
         // prettier-ignore
         switch (this.dataVizView) {
@@ -113,10 +113,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     }
 
     fetchData() {
-        if (DataVizBox.pairSet.has(StrCast(this.rootDoc.fileUpload))) return;
-        DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), []);
+        if (DataVizBox.pairSet.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) return;
+        DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []);
         fetch('/csvData?uri=' + this.dataUrl?.url.href) //
-            .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), res))));
+            .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res))));
     }
 
     // handle changing the view using a button
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 777bf2f66..11f62a61f 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -56,8 +56,8 @@ export class LineChart extends React.Component<LineChartProps> {
     }
     @computed get incomingLinks() {
         return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
-            .filter(link => link.anchor1 !== this.props.rootDoc) // get links where this chart doc is the target of the link
-            .map(link => DocCast(link.anchor1)); // then return the source of the link
+            .filter(link => link.link_anchor_1 !== this.props.rootDoc) // get links where this chart doc is the target of the link
+            .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
     }
     @computed get incomingSelected() {
         return this.incomingLinks // all links that are pointing to this node
@@ -87,7 +87,7 @@ export class LineChart extends React.Component<LineChartProps> {
             { fireImmediately: true }
         );
         this._disposers.annos = reaction(
-            () => DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']),
+            () => DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']),
             annotations => {
                 // modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way
                 // could be blue colored to make it look like anchor
@@ -123,7 +123,7 @@ export class LineChart extends React.Component<LineChartProps> {
             element.classList.remove('selected');
         }
     };
-    // gets called whenever the "data-annotations" fields gets updated
+    // gets called whenever the "data_annotations" fields gets updated
     drawAnnotations = (dataX: number, dataY: number, selected?: boolean) => {
         // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements
         // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY
@@ -228,15 +228,15 @@ export class LineChart extends React.Component<LineChartProps> {
 
         // creating the x and y scales
         const xScale = scaleCreatorNumerical(xMin, xMax, 0, width);
-        const yScale = scaleCreatorNumerical(0, yMax,height, 0);
+        const yScale = scaleCreatorNumerical(0, yMax, height, 0);
 
         // adding svg
         const margin = this.props.margin;
         const svg = (this._lineChartSvg = d3
             .select(this._lineChartRef.current)
             .append('svg')
-            .attr('width', `${width +margin.left + margin.right}`)
-            .attr('height', `${height + margin.top + margin.bottom }`)
+            .attr('width', `${width + margin.left + margin.right}`)
+            .attr('height', `${height + margin.top + margin.bottom}`)
             .append('g')
             .attr('transform', `translate(${margin.left}, ${margin.top})`));
 
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 0d69ac890..3816bddfa 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -47,17 +47,17 @@ export class TableBox extends React.Component<TableBoxProps> {
                                                     e => {
                                                         const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
                                                         const targetCreator = (annotationOn: Doc | undefined) => {
-                                                            const alias = Doc.MakeAlias(this.props.docView?.()!.rootDoc!);
-                                                            alias._dataVizView = DataVizView.LINECHART;
-                                                            alias._dataVizAxes = new List<string>([col, col]);
-                                                            alias.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
-                                                            return alias;
+                                                            const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
+                                                            embedding._dataVizView = DataVizView.LINECHART;
+                                                            embedding._data_vizAxes = new List<string>([col, col]);
+                                                            embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
+                                                            return embedding;
                                                         };
                                                         if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
                                                             DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
                                                                 dragComplete: e => {
                                                                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
-                                                                        e.linkDocument.linkDisplay = true;
+                                                                        e.linkDocument.layout_linkDisplay = true;
                                                                         // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
                                                                         // e.annoDragData.linkSourceDoc.followLinkZoom = false;
                                                                     }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 76a5ce7b3..348ef910a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -120,15 +120,15 @@ export class DocumentContentsView extends React.Component<
         select: (ctrl: boolean) => void;
         NativeDimScaling?: () => number;
         setHeight?: (height: number) => void;
-        layoutKey: string;
+        layout_fieldKey: string;
     }
 > {
     @computed get layout(): string {
         TraceMobx();
         if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
         if (!this.layoutDoc) return '<p>awaiting layout</p>';
-        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString());
-        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, 'layout')], 'string');
+        if (this.props.layout_fieldKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString());
+        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layout_fieldKey ? this.props.layout_fieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string');
         if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString();
         if (typeof layout === 'string') return layout;
         return '<p>Loading layout</p>';
@@ -140,12 +140,12 @@ export class DocumentContentsView extends React.Component<
     }
     get layoutDoc() {
         // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
-        // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+        // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined);
         const template: Doc =
             this.props.LayoutTemplate?.() ||
             (this.props.LayoutTemplateString && this.props.Document) ||
-            (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
-            Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+            (this.props.layout_fieldKey && StrCast(this.props.Document[this.props.layout_fieldKey]) && this.props.Document) ||
+            Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined);
         return Doc.expandTemplateLayout(template, this.props.Document);
     }
 
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 47705d53d..d5ca30957 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -52,7 +52,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                         dragComplete: dropEv => {
                             if (this.props.View && dropEv.linkDocument) {
                                 // dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
-                                !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = 'hyperlink');
+                                !dropEv.linkDocument.link_relationship && (Doc.GetProto(dropEv.linkDocument).link_relationship = 'hyperlink');
                             }
                             linkDrag?.end();
                         },
@@ -142,7 +142,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             } else if (startLink !== endLink) {
                 endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
                 startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
-                const linkDoc = DocUtils.MakeLink(startLink, endLink, { linkRelationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });
+                const linkDoc = DocUtils.MakeLink(startLink, endLink, { link_relationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });
 
                 LinkManager.currentLink = linkDoc;
 
@@ -197,7 +197,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         const results = [] as Doc[];
         const filters = this.props.View.props.docFilters();
         Array.from(new Set<Doc>(this.props.View.allLinks)).forEach(link => {
-            if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.anchor2 as Doc], filters, []).length || DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) {
+            if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.link_anchor_2 as Doc], filters, []).length || DocUtils.FilterDocs([link.link_anchor_1 as Doc], filters, []).length) {
                 results.push(link);
             }
         });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ffbde18ca..d8494e58e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -131,7 +131,7 @@ export interface DocComponentView {
     setData?: (data: Field | Promise<RefField | undefined>) => boolean;
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     incrementalRendering?: () => void;
-    fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
+    layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
     overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document
     fieldKey?: string;
     annotationKey?: string;
@@ -150,7 +150,7 @@ export interface DocumentViewSharedProps {
     Document: Doc;
     DataDoc?: Doc;
     contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
-    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
+    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
     suppressSetHeight?: boolean;
     thumbShown?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
@@ -164,11 +164,11 @@ export interface DocumentViewSharedProps {
     styleProvider: Opt<StyleProviderFunc>;
     setTitleFocus?: () => void;
     focus: DocFocusFunc;
-    fitWidth?: (doc: Doc) => boolean | undefined;
+    layout_fitWidth?: (doc: Doc) => boolean | undefined;
     docFilters: () => string[];
     docRangeFilters: () => string[];
     searchFilterDocs: () => Doc[];
-    showTitle?: () => string;
+    layout_showTitle?: () => string;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
@@ -275,8 +275,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     public get LayoutFieldKey() {
         return Doc.LayoutFieldKey(this.layoutDoc);
     }
-    @computed get ShowTitle() {
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
+    @computed get layout_showTitle() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.layout_showTitle) as Opt<string>;
     }
     @computed get NativeDimScaling() {
         return this.props.NativeDimScaling?.() || 1;
@@ -305,8 +305,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get headerMargin() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
     }
-    @computed get showCaption() {
-        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.ShowCaption) || 0;
+    @computed get layout_showCaption() {
+        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.showCaption) || 0;
     }
     @computed get titleHeight() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
@@ -315,7 +315,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
     }
     @computed get finalLayoutKey() {
-        return StrCast(this.Document.layoutKey, 'layout');
+        return StrCast(this.Document.layout_fieldKey, 'layout');
     }
     @computed get nativeWidth() {
         return this.props.NativeWidth();
@@ -410,7 +410,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     // switches text input focus to the title bar of the document (and displays the title bar if it hadn't been)
     setTitleFocus = () => {
-        if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = 'title';
+        if (!StrCast(this.layoutDoc._layout_showTitle)) this.layoutDoc._layout_showTitle = 'title';
         setTimeout(() => this._titleRef.current?.setIsFocused(true)); // use timeout in case title wasn't shown to allow re-render so that titleref will be defined
     };
 
@@ -539,7 +539,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
             this.cleanupPointerEvents();
-            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
+            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
         }
     };
 
@@ -589,7 +589,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
     @undoBatch setToggleDetail = () =>
         (this.Document.onClick = ScriptField.MakeScript(
-            `toggleDetail(documentView, "${StrCast(this.Document.layoutKey)
+            `toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
                 .replace('layout_', '')
                 .replace(/^layout$/, 'detail')}")`,
             { documentView: 'any' }
@@ -611,7 +611,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
                     de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
                 }
-                if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
+                if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.embedContainer) {
                     const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                 }
@@ -622,12 +622,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch
     @action
     makeIntoPortal = () => {
-        const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from');
+        const portalLink = this.allLinks.find(d => d.link_anchor_1 === this.props.Document && d.link_relationship === 'portal to:portal from');
         if (!portalLink) {
             DocUtils.MakeLink(
                 this.props.Document,
-                Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }),
-                { linkRelationship: 'portal to:portal from' }
+                Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _layout_fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }),
+                { link_relationship: 'portal to:portal from' }
             );
         }
         this.Document.followLinkLocation = OpenWhere.lightbox;
@@ -699,7 +699,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             .forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
 
         if (!this.props.Document.isFolder) {
-            const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
+            const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layout_fieldKey)], Doc, null);
             const appearance = cm.findByDescription('UI Controls...');
             const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
@@ -742,7 +742,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
             const funcs: ContextMenuProps[] = [];
             if (!Doc.noviceMode && this.layoutDoc.onDragStart) {
-                funcs.push({ description: 'Drag an Alias', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
+                funcs.push({ description: 'Drag an Embedding', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getEmbedding(this.dragFactory)')) });
                 funcs.push({ description: 'Drag a Copy', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
                 funcs.push({ description: 'Drag Document', icon: 'edit', event: () => (this.layoutDoc.onDragStart = undefined) });
                 cm.addItem({ description: 'OnDrag...', noexpand: true, subitems: funcs, icon: 'asterisk' });
@@ -910,7 +910,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     onClick={this.onClickFunc}
                     focus={this.props.focus}
                     setTitleFocus={this.setTitleFocus}
-                    layoutKey={this.finalLayoutKey}
+                    layout_fieldKey={this.finalLayoutKey}
                 />
                 {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
             </div>
@@ -922,7 +922,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         // prettier-ignore
         switch (property.split(':')[0]) {
-            case StyleProp.ShowTitle:     return '';
+            case StyleProp.layout_showTitle:     return '';
             case StyleProp.PointerEvents: return 'none';
             case StyleProp.Highlighting:  return undefined;
         }
@@ -938,22 +938,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         TraceMobx();
         return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(
             link =>
-                Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
-                Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
-                ((link.anchor1 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor1 as Doc)?.annotationOn as Doc, this.rootDoc)) ||
-                ((link.anchor2 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor2 as Doc)?.annotationOn as Doc, this.rootDoc))
+                Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.rootDoc) ||
+                Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.rootDoc) ||
+                ((link.link_anchor_1 as Doc)?.unrendered && Doc.AreProtosEqual((link.link_anchor_1 as Doc)?.annotationOn as Doc, this.rootDoc)) ||
+                ((link.link_anchor_2 as Doc)?.unrendered && Doc.AreProtosEqual((link.link_anchor_2 as Doc)?.annotationOn as Doc, this.rootDoc))
         );
     }
     @computed get allLinks() {
         TraceMobx();
         return LinkManager.Instance.getAllRelatedLinks(this.rootDoc);
     }
-    hideLink = computedFn((link: Doc) => () => (link.linkDisplay = false));
+    hideLink = computedFn((link: Doc) => () => (link.layout_linkDisplay = false));
     @computed get allLinkEndpoints() {
         // the small blue dots that mark the endpoints of links
         TraceMobx();
-        if (this.props.hideLinkAnchors || this.layoutDoc.hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.unrendered) return null;
-        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.linkDisplay);
+        if (this.props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.unrendered) return null;
+        const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.layout_linkDisplay);
         return filtered.map(link => (
             <div className="documentView-anchorCont" key={link[Id]}>
                 <DocumentView
@@ -964,14 +964,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     PanelWidth={this.anchorPanelWidth}
                     PanelHeight={this.anchorPanelHeight}
                     dontRegisterView={false}
-                    showTitle={returnEmptyString}
+                    layout_showTitle={returnEmptyString}
                     hideCaptions={true}
                     hideLinkAnchors={true}
-                    fitWidth={returnTrue}
+                    layout_fitWidth={returnTrue}
                     removeDocument={this.hideLink(link)}
                     styleProvider={this.anchorStyleProvider}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(link, this.rootDoc)}`)}
+                    LayoutTemplateString={LinkAnchorBox.LayoutString(`link_anchor_${Doc.LinkEndpoint(link, this.rootDoc)}`)}
                 />
             </div>
         ));
@@ -1046,9 +1046,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get innards() {
         TraceMobx();
         const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1;
-        const showTitle = this.ShowTitle?.split(':')[0];
-        const showTitleHover = this.ShowTitle?.includes(':hover');
-        const captionView = !this.showCaption ? null : (
+        const layout_showTitle = this.layout_showTitle?.split(':')[0];
+        const layout_showTitleHover = this.layout_showTitle?.includes(':hover');
+        const captionView = !this.layout_showCaption ? null : (
             <div
                 className="documentView-captionWrapper"
                 style={{
@@ -1060,7 +1060,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     {...this.props}
                     yPadding={10}
                     xPadding={10}
-                    fieldKey={this.showCaption}
+                    fieldKey={this.layout_showCaption}
                     fontSize={12 * Math.max(1, (2 * ffscale()) / 3)}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
@@ -1071,27 +1071,27 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 />
             </div>
         );
-        const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
+        const targetDoc = layout_showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
         const background = StrCast(
             SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
-            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)'
+            Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)'
         );
-        const sidebarWidthPercent = +StrCast(this.layoutDoc.sidebarWidthPercent).replace('%', '');
-        const titleView = !showTitle ? null : (
+        const layout_sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
+        const titleView = !layout_showTitle ? null : (
             <div
-                className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
+                className={`documentView-titleWrapper${layout_showTitleHover ? '-hover' : ''}`}
                 key="title"
                 style={{
                     position: this.headerMargin ? 'relative' : 'absolute',
                     height: this.titleHeight,
-                    width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button
+                    width: !this.headerMargin ? `calc(${layout_sidebarWidthPercent || 100}% - 18px)` : (layout_sidebarWidthPercent || 100) + '%', // leave room for annotation button
                     color: lightOrDark(background),
                     background,
                     pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                 }}>
                 <EditableView
                     ref={this._titleRef}
-                    contents={showTitle
+                    contents={layout_showTitle
                         .split(';')
                         .map(field => field.trim())
                         .map(field => targetDoc[field]?.toString())
@@ -1100,27 +1100,27 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     fontSize={10}
                     GetValue={() => {
                         this.props.select(false);
-                        return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle;
+                        return layout_showTitle.split(';').length === 1 ? layout_showTitle + '=' + Field.toString(targetDoc[layout_showTitle.split(';')[0]] as any as Field) : '#' + layout_showTitle;
                     }}
                     SetValue={undoBatch((input: string) => {
                         if (input?.startsWith('#')) {
-                            if (this.props.showTitle) {
-                                this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
+                            if (this.props.layout_showTitle) {
+                                this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined;
                             } else {
-                                Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : 'creationDate';
+                                Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'creationDate';
                             }
                         } else {
-                            var value = input.replace(new RegExp(showTitle + '='), '') as string | number;
-                            if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
-                            if (showTitle.includes('Date') || showTitle === 'author') return true;
-                            Doc.SetInPlace(targetDoc, showTitle, value, true);
+                            var value = input.replace(new RegExp(layout_showTitle + '='), '') as string | number;
+                            if (layout_showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
+                            if (layout_showTitle.includes('Date') || layout_showTitle === 'author') return true;
+                            Doc.SetInPlace(targetDoc, layout_showTitle, value, true);
                         }
                         return true;
                     })}
                 />
             </div>
         );
-        return this.props.hideTitle || (!showTitle && !this.showCaption) ? (
+        return this.props.hideTitle || (!layout_showTitle && !this.layout_showCaption) ? (
             this.contents
         ) : (
             <div className="documentView-styleWrapper">
@@ -1319,11 +1319,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     get LayoutFieldKey() {
         return this.docView?.LayoutFieldKey || 'layout';
     }
-    @computed get fitWidth() {
-        return this.docView?._componentView?.fitWidth?.() ?? this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth;
+    @computed get layout_fitWidth() {
+        return this.docView?._componentView?.layout_fitWidth?.() ?? this.props.layout_fitWidth?.(this.rootDoc) ?? this.layoutDoc?.layout_fitWidth;
     }
     @computed get anchorViewDoc() {
-        return this.props.LayoutTemplateString?.includes('anchor2') ? DocCast(this.rootDoc['anchor2']) : this.props.LayoutTemplateString?.includes('anchor1') ? DocCast(this.rootDoc['anchor1']) : undefined;
+        return this.props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.rootDoc['link_anchor_2']) : this.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.rootDoc['link_anchor_1']) : undefined;
     }
     @computed get hideLinkButton() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.isSelected() ? ':selected' : ''));
@@ -1339,13 +1339,13 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return Doc.Layout(this.Document, this.props.LayoutTemplate?.());
     }
     @computed get nativeWidth() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
     }
     @computed get nativeHeight() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
     }
     @computed get shouldNotScale() {
-        return (this.fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+        return (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._viewType as any);
     }
     @computed get effectiveNativeWidth() {
         return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width);
@@ -1356,8 +1356,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get nativeScaling() {
         if (this.shouldNotScale) return 1;
         const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
-        if (this.fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
-            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or fitWidth
+        if (this.layout_fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
+            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or layout_fitWidth
         }
         return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled
     }
@@ -1365,7 +1365,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth();
     }
     @computed get panelHeight() {
-        if (this.effectiveNativeHeight && (!this.fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) {
+        if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) {
             return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling);
         }
         return this.props.PanelHeight();
@@ -1374,7 +1374,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.effectiveNativeWidth ? Math.max(0, (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2) : 0;
     }
     @computed get Yshift() {
-        return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && (!this.layoutDoc.nativeHeightUnfrozen || (!this.fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
+        return this.effectiveNativeWidth &&
+            this.effectiveNativeHeight &&
+            Math.abs(this.Xshift) < 0.001 &&
+            (!this.layoutDoc.nativeHeightUnfrozen || (!this.layout_fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight()))
             ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2)
             : 0;
     }
@@ -1410,10 +1413,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             finished?.();
             this.docView && (this.docView._animateScaleTime = animTime);
         });
-        const layoutKey = Cast(this.Document.layoutKey, 'string', null);
-        if (layoutKey !== 'layout_icon') {
+        const layout_fieldKey = Cast(this.Document.layout_fieldKey, 'string', null);
+        if (layout_fieldKey !== 'layout_icon') {
             this.switchViews(true, 'icon', finalFinished);
-            if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') this.Document.deiconifyLayout = layoutKey.replace('layout_', '');
+            if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') this.Document.deiconifyLayout = layout_fieldKey.replace('layout_', '');
         } else {
             const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
             this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished);
@@ -1433,7 +1436,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         setTimeout(
             action(() => {
                 if (useExistingLayout && custom && this.rootDoc['layout_' + view]) {
-                    this.rootDoc.layoutKey = 'layout_' + view;
+                    this.rootDoc.layout_fieldKey = 'layout_' + view;
                 } else {
                     this.setCustomView(custom, view);
                 }
@@ -1520,7 +1523,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             width: xshift ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
                             height: this.props.forceAutoHeight
                                 ? undefined
-                                : yshift ?? (this.fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
+                                : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
                         }}>
                         <DocumentViewInternal
                             {...this.props}
@@ -1558,7 +1561,7 @@ ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView
 });
 
 ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
-    if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
+    if (dv.Document.layout_fieldKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
     else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
 });
 
@@ -1571,12 +1574,12 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
         const other = LinkManager.getOppositeAnchor(link, linkSource);
         const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other;
         if (otherdoc && !collectedLinks?.some(d => Doc.AreProtosEqual(d, otherdoc))) {
-            const alias = Doc.MakeAlias(otherdoc);
-            alias.x = wid;
-            alias.y = 0;
-            alias._lockedPosition = false;
+            const embedding = Doc.MakeEmbedding(otherdoc);
+            embedding.x = wid;
+            embedding.y = 0;
+            embedding._lockedPosition = false;
             wid += otherdoc[WidthSym]();
-            Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', alias);
+            Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', embedding);
         }
     });
     return links;
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index 163c5a9ed..f17ab06e7 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -90,7 +90,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
     render() {
         TraceMobx();
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
         return (
             <div
                 ref={r => this.updateSize()}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 7eae4e938..85dd779fc 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,7 +50,7 @@ export class FieldView extends React.Component<FieldViewProps> {
 
     @computed
     get field(): FieldResult {
-        const { Document, fieldKey } = this.props;
+        const { Document, fieldKey: fieldKey } = this.props;
         return Document[fieldKey];
     }
     render() {
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index b43e359ff..db72209ab 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -90,7 +90,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps>
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc);
         }
-        // if (this.autoHeight) this.tryUpdateScrollHeight();
+        // if (this.layout_autoHeight) this.tryUpdateScrollHeight();
     };
     @computed get theGraph() {
         return <div id={`${this._plotId}`} ref={r => r && this.createGraph(r)} style={{ position: 'absolute', width: '100%', height: '100%' }} onPointerDown={e => e.stopPropagation()} />;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c9be10d3a..7d1d50cc7 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -79,9 +79,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
             Docs.Create.ImageanchorDocument({
                 title: 'ImgAnchor:' + this.rootDoc.title,
-                presPanX: NumCast(this.layoutDoc._panX),
-                presPanY: NumCast(this.layoutDoc._panY),
-                presViewScale: Cast(this.layoutDoc._viewScale, 'number', null),
+                presPanX: NumCast(this.layoutDoc._freeform_panX),
+                presPanY: NumCast(this.layoutDoc._freeform_panY),
+                presViewScale: Cast(this.layoutDoc._freeform_scale, 'number', null),
                 presTransition: 1000,
                 unrendered: true,
                 annotationOn: this.rootDoc,
@@ -99,7 +99,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._disposers.sizer = reaction(
             () => ({
                 forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
-                scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._viewScale, 1),
+                scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._freeform_scale, 1),
                 selected: this.props.isSelected(),
             }),
             ({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'),
@@ -116,7 +116,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(
-            () => this.layoutDoc._scrollTop,
+            () => this.layoutDoc.layout_scrollTop,
             s_top => {
                 this._forcedScroll = true;
                 !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(s_top));
@@ -144,7 +144,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 de.complete.docDragData.droppedDocuments.forEach(
                     action((drop: Doc) => {
                         Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
-                        this.rootDoc[this.fieldKey + '-usePath'] = 'alternate:hover';
+                        this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover';
                         e.stopPropagation();
                     })
                 );
@@ -167,28 +167,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @undoBatch
     setNativeSize = action(() => {
-        const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._viewScale, 1);
+        const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._freeform_scale, 1);
         const nscale = NumCast(this.props.PanelWidth()) / scaling;
-        const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
-        const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
-        this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh;
-        this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nh;
-        this.rootDoc._panX = nh * NumCast(this.rootDoc._panX);
-        this.rootDoc._panY = nh * NumCast(this.rootDoc._panY);
-        this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined;
-        this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined;
-        this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined;
-        this.dataDoc._panYMin = this.dataDoc._panYMin ? nw * NumCast(this.dataDoc._panYMin) : undefined;
+        const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']);
+        const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+        this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nh;
+        this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nh;
+        this.rootDoc._freeform_panX = nh * NumCast(this.rootDoc._freeform_panX);
+        this.rootDoc._freeform_panY = nh * NumCast(this.rootDoc._freeform_panY);
+        this.dataDoc._freeform_panXMax = this.dataDoc._freeform_panXMax ? nh * NumCast(this.dataDoc._freeform_panXMax) : undefined;
+        this.dataDoc._freeform_panXMin = this.dataDoc._freeform_panXMin ? nh * NumCast(this.dataDoc._freeform_panXMin) : undefined;
+        this.dataDoc._freeform_panYMax = this.dataDoc._freeform_panYMax ? nw * NumCast(this.dataDoc._freeform_panYMax) : undefined;
+        this.dataDoc._freeform_panYMin = this.dataDoc._freeform_panYMin ? nw * NumCast(this.dataDoc._freeform_panYMin) : undefined;
     });
     @undoBatch
     rotate = action(() => {
-        const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
-        const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
+        const nw = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+        const nh = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']);
         const w = this.layoutDoc._width;
         const h = this.layoutDoc._height;
         this.dataDoc[this.fieldKey + '-rotation'] = (NumCast(this.dataDoc[this.fieldKey + '-rotation']) + 90) % 360;
-        this.dataDoc[this.fieldKey + '-nativeWidth'] = nh;
-        this.dataDoc[this.fieldKey + '-nativeHeight'] = nw;
+        this.dataDoc[this.fieldKey + '_nativeWidth'] = nh;
+        this.dataDoc[this.fieldKey + '_nativeHeight'] = nw;
         this.layoutDoc._width = h;
         this.layoutDoc._height = w;
     });
@@ -204,7 +204,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const anchy = NumCast(cropping.y);
         const anchw = NumCast(cropping._width);
         const anchh = NumCast(cropping._height);
-        const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw;
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
@@ -213,24 +213,24 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
-        croppingProto.isPrototype = true;
+        croppingProto.isDataDoc = true;
         croppingProto.backgroundColor = undefined;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
         croppingProto.layout = ImageBox.LayoutString('data');
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
-        croppingProto['data-nativeWidth'] = anchw;
-        croppingProto['data-nativeHeight'] = anchh;
-        croppingProto.viewScale = viewScale;
-        croppingProto.viewScaleMin = viewScale;
-        croppingProto.panX = anchx / viewScale;
-        croppingProto.panY = anchy / viewScale;
-        croppingProto.panXMin = anchx / viewScale;
-        croppingProto.panXMax = anchw / viewScale;
-        croppingProto.panYMin = anchy / viewScale;
-        croppingProto.panYMax = anchh / viewScale;
+        croppingProto['data_nativeWidth'] = anchw;
+        croppingProto['data_nativeHeight'] = anchh;
+        croppingProto.freeform_scale = viewScale;
+        croppingProto.freeform_scaleMin = viewScale;
+        croppingProto.freeform_panX = anchx / viewScale;
+        croppingProto.freeform_panY = anchy / viewScale;
+        croppingProto.freeform_panXMin = anchx / viewScale;
+        croppingProto.freeform_panXMax = anchw / viewScale;
+        croppingProto.freeform_panYMin = anchy / viewScale;
+        croppingProto.freeform_panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
+            DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
             cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]();
             cropping.y = NumCast(this.rootDoc.y);
             this.props.addDocTab(cropping, OpenWhere.inParent);
@@ -312,7 +312,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
     };
 
-    getScrollHeight = () => (this.props.fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._viewScale, 1) === NumCast(this.rootDoc._viewScaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
+    getScrollHeight = () => (this.props.layout_fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._freeform_scale, 1) === NumCast(this.rootDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
 
     @computed
     private get considerDownloadIcon() {
@@ -358,13 +358,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @computed get nativeSize() {
         TraceMobx();
-        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth'], 500));
-        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight'], 500));
+        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500));
+        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500));
         const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '-nativeOrientation'], 1);
         return { nativeWidth, nativeHeight, nativeOrientation };
     }
     @computed get overlayImageIcon() {
-        const usePath = this.rootDoc[`_${this.fieldKey}-usePath`];
+        const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
         return (
             <Tooltip
                 title={
@@ -385,7 +385,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 <div
                     className="imageBox-alternateDropTarget"
                     ref={this._overlayIconRef}
-                    onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}-usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
+                    onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
                     style={{
                         display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
                         width: 'min(10%, 25px)',
@@ -431,7 +431,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             transformOrigin = 'right top';
             transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         }
-        const usePath = this.rootDoc[`_${this.fieldKey}-usePath`];
+        const usePath = this.rootDoc[`_${this.fieldKey}_usePath`];
 
         return (
             <div className="imageBox-cont" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
@@ -459,9 +459,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         TraceMobx();
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
-    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop) * this.props.ScreenToLocalTransform().Scale);
+    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.props.ScreenToLocalTransform().Scale);
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._freeform_scale, 1) <= NumCast(this.rootDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -497,9 +497,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 ref={this._mainCont}
                 onScroll={action(e => {
                     if (!this._forcedScroll) {
-                        if (this.layoutDoc._scrollTop || this._mainCont.current?.scrollTop) {
+                        if (this.layoutDoc._layout_scrollTop || this._mainCont.current?.scrollTop) {
                             this._ignoreScroll = true;
-                            this.layoutDoc._scrollTop = this._mainCont.current?.scrollTop;
+                            this.layoutDoc._layout_scrollTop = this._mainCont.current?.scrollTop;
                             this._ignoreScroll = false;
                         }
                     }
@@ -510,7 +510,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     height: this.props.PanelWidth() ? undefined : `100%`,
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
                     borderRadius,
-                    overflow: this.layoutDoc.fitWidth || this.props.fitWidth?.(this.rootDoc) ? 'auto' : undefined,
+                    overflow: this.layoutDoc.layout_fitWidth || this.props.layout_fitWidth?.(this.rootDoc) ? 'auto' : undefined,
                 }}>
                 <CollectionFreeFormView
                     ref={this._ffref}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index f3c8ba62d..88a82e8e6 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -46,15 +46,13 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
     reverseNativeScaling = returnTrue;
     able = returnAlways;
-    fitWidth = returnTrue;
+    layout_fitWidth = returnTrue;
     overridePointerEvents = returnAll;
     onClickScriptDisable = returnAlways;
 
     @observable private rows: KeyValuePair[] = [];
+    @observable _splitPercentage = 50;
 
-    @computed get splitPercentage() {
-        return NumCast(this.props.Document.schemaSplitPercentage, 50);
-    }
     get fieldDocToLayout() {
         return this.props.fieldKey ? DocCast(this.props.Document[this.props.fieldKey], DocCast(this.props.Document)) : this.props.Document;
     }
@@ -147,7 +145,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         const rows: JSX.Element[] = [];
         let i = 0;
         const self = this;
-        for (const key of Object.keys(ids).slice().sort()) {
+        const keys = Object.keys(ids).slice();
+        //for (const key of [...keys.filter(id => id !== 'layout' && !id.includes('_')).sort(), ...keys.filter(id => id === 'layout' || id.includes('_')).sort()]) {
+        for (const key of keys.sort()) {
             rows.push(
                 <KeyValuePair
                     doc={realDoc}
@@ -162,7 +162,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                             if (el) self.rows.push(el);
                         };
                     })()}
-                    keyWidth={100 - this.splitPercentage}
+                    keyWidth={100 - this._splitPercentage}
                     rowStyle={'keyValueBox-' + (i++ % 2 ? 'oddRow' : 'evenRow')}
                     key={key}
                     keyName={key}
@@ -180,7 +180,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                         this._keyInput.current!.select();
                         e.stopPropagation();
                     }}
-                    style={{ width: `${100 - this.splitPercentage}%` }}>
+                    style={{ width: `${100 - this._splitPercentage}%` }}>
                     <input style={{ width: '100%' }} ref={this._keyInput} type="text" placeholder="Key" />
                 </td>
                 <td
@@ -189,7 +189,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                         this._valInput.current!.select();
                         e.stopPropagation();
                     }}
-                    style={{ width: `${this.splitPercentage}%` }}>
+                    style={{ width: `${this._splitPercentage}%` }}>
                     <input style={{ width: '100%' }} ref={this._valInput} type="text" placeholder="Value" onKeyDown={this.onEnterKey} />
                 </td>
             </tr>
@@ -199,7 +199,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     @action
     onDividerMove = (e: PointerEvent): void => {
         const nativeWidth = this._mainCont.current!.getBoundingClientRect();
-        this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100));
+        this._splitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100));
     };
     @action
     onDividerUp = (e: PointerEvent): void => {
@@ -216,7 +216,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     getFieldView = async () => {
         const rows = this.rows.filter(row => row.isChecked);
         if (rows.length > 1) {
-            const parent = Docs.Create.StackingDocument([], { _autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true });
+            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true });
             for (const row of rows) {
                 const field = this.createFieldView(DocCast(this.props.Document.data), row);
                 field && Doc.AddDocToList(parent, 'data', field);
@@ -229,9 +229,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
         const metaKey = row.props.keyName;
-        const fieldTemplate = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeAlias(templateDoc);
+        const fieldTemplate = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeEmbedding(templateDoc);
         fieldTemplate.title = metaKey;
-        fieldTemplate.fitWidth = true;
+        fieldTemplate.layout_fitWidth = true;
         fieldTemplate._xMargin = 10;
         fieldTemplate._yMargin = 10;
         fieldTemplate._width = 100;
@@ -284,8 +284,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
 
     render() {
         const dividerDragger =
-            this.splitPercentage === 0 ? null : (
-                <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
+            this._splitPercentage === 0 ? null : (
+                <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this._splitPercentage}% - 5px), 0px)` }}>
                     <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
                 </div>
             );
@@ -295,10 +295,10 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
                 <table className="keyValueBox-table">
                     <tbody className="keyValueBox-tbody">
                         <tr className="keyValueBox-header">
-                            <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }} ref={this._keyHeader} onPointerDown={SetupDrag(this._keyHeader, this.getFieldView)}>
+                            <th className="keyValueBox-key" style={{ width: `${100 - this._splitPercentage}%` }} ref={this._keyHeader} onPointerDown={SetupDrag(this._keyHeader, this.getFieldView)}>
                                 Key
                             </th>
-                            <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>
+                            <th className="keyValueBox-fields" style={{ width: `${this._splitPercentage}%` }}>
                                 Fields
                             </th>
                         </tr>
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 57d36932e..2b4867321 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -19,6 +19,7 @@
             position: relative;
             margin: 0;
         }
+        .keyValuePair-keyFieldMod,
         .keyValuePair-keyField {
             width: 100%;
             margin-left: 20px;
@@ -27,6 +28,9 @@
             overflow: auto;
             display: inline;
         }
+        .keyValuePair-keyFieldMod {
+            margin-left: 35px;
+        }
     }
 }
 .keyValuePair-td-value {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 85641124f..509bb667e 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -66,7 +66,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'alias',
+            dropAction: 'embed',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
@@ -108,8 +108,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                             })}>
                             X
                         </button>
-                        <input className={'keyValuePair-td-key-check'} type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
-                        <div className="keyValuePair-keyField" style={{ color: keyStyle }}>
+                        <input className="keyValuePair-td-key-check" type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
+                        <div className={`keyValuePair-keyField${props.fieldKey.includes('_') ? 'Mod' : ''}`} style={{ color: keyStyle }}>
                             {'('.repeat(parenCount)}
                             {props.fieldKey}
                             {')'.repeat(parenCount)}
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 916458dfd..32026ea9c 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -37,7 +37,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
     }
 
     getTitle() {
-        return this.rootDoc['title-custom'] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
+        return this.rootDoc.title_custom ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title);
     }
 
     protected createDropTarget = (ele: HTMLDivElement) => {
@@ -82,7 +82,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
         return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
     }
 
-    fitTextToBox = (r: any): any => {
+    fitTextToBox = (r: any) => {
         const singleLine = BoolCast(this.rootDoc._singleLine, true);
         const params = {
             rotateText: null,
@@ -141,9 +141,9 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp
                         paddingBottom: NumCast(this.rootDoc._yPadding),
                         width: this.props.PanelWidth(),
                         height: this.props.PanelHeight(),
-                        whiteSpace: boxParams.singleLine ? 'pre' : 'pre-wrap',
+                        whiteSpace: typeof boxParams !== 'number' && boxParams.singleLine ? 'pre' : 'pre-wrap',
                     }}>
-                    <span style={{ width: boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
+                    <span style={{ width: typeof boxParams !== 'number' && boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}>
                         {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')}
                     </span>
                 </div>
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 31f1775e5..f38ef634c 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -46,14 +46,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.rootDoc]);
-                dragData.dropAction = 'alias';
-                dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'onClick'];
+                dragData.dropAction = 'embed';
+                dragData.removeDropProperties = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                 return true;
             } else {
                 this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
                 this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
-                this.rootDoc.linkAutoMove = false;
+                this.rootDoc.layout_autoMoveAnchors = false;
             }
         }
         return false;
@@ -67,10 +67,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const x = NumCast(this.rootDoc[this.fieldKey + '_x'], 100);
         const y = NumCast(this.rootDoc[this.fieldKey + '_y'], 100);
         const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor');
-        const anchor = this.fieldKey === 'anchor1' ? 'anchor2' : 'anchor1';
-        const anchorScale = !this.dataDoc[this.fieldKey + '-useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
+        const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
+        const anchorScale = !this.dataDoc[this.fieldKey + '_useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
         const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
-        const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor1') ? 'anchor1' : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor2') ? 'anchor2' : '';
+        const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_1')
+            ? 'link_anchor_1'
+            : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_2')
+            ? 'link_anchor_2'
+            : '';
         return (
             <div
                 ref={this._ref}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 46ccdecae..710d41471 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -9,7 +9,7 @@ import './LinkBox.scss';
 
 @observer
 export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) {
+    public static LayoutString(fieldKey: string = 'link') {
         return FieldView.LayoutString(LinkBox, fieldKey);
     }
 
@@ -23,7 +23,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
             <div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
                 <ComparisonBox
                     {...this.props}
-                    fieldKey="anchor"
+                    fieldKey="link_anchor"
                     setHeight={emptyFunction}
                     dontRegisterView={true}
                     renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index 91bd505c5..c45045a8a 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -24,7 +24,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
     onDismiss = (add: boolean) => {
         LinkDescriptionPopup.descriptionPopup = false;
         if (add) {
-            LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).description = this.description);
+            LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).link_description = this.description);
         }
     };
 
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c58b5dd8c..c6172ee01 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -58,13 +58,13 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         var linkTarget = this.props.linkDoc;
         this._linkSrc = this.props.linkSrc;
         this._linkDoc = this.props.linkDoc;
-        const anchor1 = this._linkDoc?.anchor1 as Doc;
-        const anchor2 = this._linkDoc?.anchor2 as Doc;
-        if (anchor1 && anchor2) {
-            linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1;
+        const link_anchor_1 = this._linkDoc?.link_anchor_1 as Doc;
+        const link_anchor_2 = this._linkDoc?.link_anchor_2 as Doc;
+        if (link_anchor_1 && link_anchor_2) {
+            linkTarget = Doc.AreProtosEqual(link_anchor_1, this._linkSrc) || Doc.AreProtosEqual(link_anchor_1?.annotationOn as Doc, this._linkSrc) ? link_anchor_2 : link_anchor_1;
         }
         if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) {
-            // want to show annotation context document if annotation is not text
+            // want to show annotation embedContainer document if annotation is not text
             linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._markerTargetDoc = this._targetDoc = anno)));
         } else {
             this._markerTargetDoc = this._targetDoc = linkTarget;
@@ -111,7 +111,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                     action(anchor => {
                         if (anchor instanceof Doc && LinkManager.Links(anchor).length) {
                             this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0];
-                            const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
+                            const automaticLink = this._linkDoc.link_relationship === LinkManager.AutoKeywords;
                             if (automaticLink) {
                                 // automatic links specify the target in the link info, not the source
                                 const linkTarget = anchor;
@@ -175,7 +175,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         } else if (this.props.hrefs?.length) {
             const webDoc =
                 Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
-                Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true });
+                Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
             this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
         }
     };
@@ -208,7 +208,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                 </div>
                 <div className="linkDocPreview-title" style={{ pointerEvents: 'all' }}>
                     {StrCast(this._markerTargetDoc.title).length > 16 ? StrCast(this._markerTargetDoc.title).substr(0, 16) + '...' : StrCast(this._markerTargetDoc.title)}
-                    <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
+                    <p className="linkDocPreview-description"> {StrCast(this._linkDoc.link_description)}</p>
                 </div>
                 <div className="linkDocPreview-buttonBar" style={{ float: 'right' }}>
                     <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
@@ -263,7 +263,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             isDocumentActive={returnFalse}
                             isContentActive={returnFalse}
                             addDocument={returnFalse}
-                            showTitle={returnEmptyString}
+                            layout_showTitle={returnEmptyString}
                             removeDocument={returnFalse}
                             addDocTab={returnFalse}
                             pinToPres={returnFalse}
@@ -278,7 +278,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             pointerEvents={returnNone}
                             focus={emptyFunction}
                             whenChildContentsActiveChanged={returnFalse}
-                            ignoreAutoHeight={true} // need to ignore autoHeight otherwise autoHeight text boxes will expand beyond the preview panel size.
+                            ignoreAutoHeight={true} // need to ignore layout_autoHeight otherwise layout_autoHeight text boxes will expand beyond the preview panel size.
                             bringToFront={returnFalse}
                             NativeWidth={Doc.NativeWidth(this._targetDoc) ? () => Doc.NativeWidth(this._targetDoc) : undefined}
                             NativeHeight={Doc.NativeHeight(this._targetDoc) ? () => Doc.NativeHeight(this._targetDoc) : undefined}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 36be7d257..9b0fddce4 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -98,7 +98,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return FieldView.LayoutString(MapBox, fieldKey);
     }
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
@@ -126,7 +126,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     static _canAnnotate = true;
@@ -267,7 +267,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setTimeout(() => {
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+                this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -282,7 +282,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -292,7 +292,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     };
@@ -327,7 +327,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         console.log('print all sidebar Docs');
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
             if (doc.lat !== undefined && doc.lng !== undefined) {
@@ -351,7 +351,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @returns
      */
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        if (this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         return this.removeDocument(doc, sidebarKey);
     };
@@ -375,11 +375,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     if (this.sidebarWidth() + localDelta[0] > 0) {
                         this._showSidebar = true;
                         this.layoutDoc._width = fullWidth + localDelta[0];
-                        this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
                     } else {
                         this._showSidebar = false;
                         this.layoutDoc._width = mapWidth;
-                        this.layoutDoc._sidebarWidthPercent = '0%';
+                        this.layoutDoc._layout_sidebarWidthPercent = '0%';
                     }
                     return false;
                 }),
@@ -388,12 +388,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     };
 
-    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
-    @computed get sidebarWidthPercent() {
-        return StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    @computed get layout_sidebarWidthPercent() {
+        return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
 
     /**
@@ -453,7 +453,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={this.sidebarBtnDown}>
@@ -467,8 +467,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     toggleSidebar = () => {
         //1.2 * w * ? = .2 * w      .2/1.2
         const prevWidth = this.sidebarWidth();
-        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
-        this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+        this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+        this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
     sidebarDown = (e: React.PointerEvent) => {
@@ -476,8 +476,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
-        this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+        this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
     };
@@ -548,7 +548,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
     infoWidth = () => this.props.PanelWidth() / 5;
@@ -616,7 +616,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         //     zoom: 15,
                         // });
                     }}
-                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+                    style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
@@ -667,7 +667,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 37f9bad9e..9354f9639 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -95,7 +95,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return FieldView.LayoutString(MapBox2, fieldKey);
     }
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     @computed get inlineTextAnnotations() {
@@ -123,7 +123,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     static _canAnnotate = true;
@@ -264,7 +264,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setTimeout(() => {
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+                this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -279,7 +279,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -289,7 +289,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     };
@@ -324,7 +324,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         console.log('print all sidebar Docs');
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
             if (doc.lat !== undefined && doc.lng !== undefined) {
@@ -348,7 +348,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @returns
      */
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        if (this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         return this.removeDocument(doc, sidebarKey);
     };
@@ -372,11 +372,11 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     if (this.sidebarWidth() + localDelta[0] > 0) {
                         this._showSidebar = true;
                         this.layoutDoc._width = fullWidth + localDelta[0];
-                        this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
                     } else {
                         this._showSidebar = false;
                         this.layoutDoc._width = mapWidth;
-                        this.layoutDoc._sidebarWidthPercent = '0%';
+                        this.layoutDoc._layout_sidebarWidthPercent = '0%';
                     }
                     return false;
                 }),
@@ -385,12 +385,12 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     };
 
-    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
-    @computed get sidebarWidthPercent() {
-        return StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    @computed get layout_sidebarWidthPercent() {
+        return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
 
     /**
@@ -450,7 +450,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={this.sidebarBtnDown}>
@@ -464,8 +464,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     toggleSidebar = () => {
         //1.2 * w * ? = .2 * w      .2/1.2
         const prevWidth = this.sidebarWidth();
-        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
-        this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+        this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+        this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
     sidebarDown = (e: React.PointerEvent) => {
@@ -473,8 +473,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
-        this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+        this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
     };
@@ -545,7 +545,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
     infoWidth = () => this.props.PanelWidth() / 5;
@@ -566,7 +566,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     onPointerDown={async e => {
                         e.button === 0 && !e.ctrlKey && e.stopPropagation();
                     }}
-                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+                    style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
@@ -616,7 +616,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="MapBox2-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 7b437c7de..bc57148dc 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -32,7 +32,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
 
     addNoteClick = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
-            const newBox = Docs.Create.TextDocument('Note', { _autoHeight: true });
+            const newBox = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
             FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
             Doc.AddDocToList(this.props.place, 'data', newBox);
             this._stack?.scrollToBottom();
@@ -42,7 +42,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
     };
 
     _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
-    childFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
+    childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
     addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
     removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
     render() {
@@ -69,7 +69,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                             rootSelected={returnFalse}
                             childHideResizeHandles={returnTrue}
                             childHideDecorationTitle={returnTrue}
-                            childFitWidth={this.childFitWidth}
+                            childLayoutFitWidth={this.childLayoutFitWidth}
                             // childDocumentsActive={returnFalse}
                             removeDocument={this.removeDoc}
                             addDocument={this.addDoc}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6aa04e356..edeaba322 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -63,7 +63,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         super(props);
         const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
         const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
-        !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        !this.Document._layout_fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
         if (this.pdfUrl) {
             if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => (this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)));
             else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf)));
@@ -122,15 +122,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
-        croppingProto.isPrototype = true;
+        croppingProto.isDataDoc = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
         croppingProto.layout = ImageBox.LayoutString('data');
         croppingProto.data = new ImageField(Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png'));
-        croppingProto['data-nativeWidth'] = anchw;
-        croppingProto['data-nativeHeight'] = anchh;
+        croppingProto['data_nativeWidth'] = anchw;
+        croppingProto['data_nativeHeight'] = anchh;
         if (addCrop) {
-            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
+            DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
         }
         this.props.bringToFront(cropping);
 
@@ -140,8 +140,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             htmlString,
             anchw,
             anchh,
-            (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
-            (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
+            (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']),
+            (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']),
             4
         )
             .then((data_url: any) => {
@@ -173,15 +173,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.layoutDoc[HeightSym](),
                 this.props.PanelWidth(),
                 this.props.PanelHeight(),
-                NumCast(this.layoutDoc._scrollTop),
-                NumCast(this.rootDoc[this.fieldKey + '-nativeHeight'], 1),
+                NumCast(this.layoutDoc._layout_scrollTop),
+                NumCast(this.rootDoc[this.fieldKey + '_nativeHeight'], 1),
                 true,
                 this.layoutDoc[Id] + '-icon',
                 (iconFile: string, nativeWidth: number, nativeHeight: number) => {
                     setTimeout(() => {
                         this.dataDoc.icon = new ImageField(iconFile);
-                        this.dataDoc['icon-nativeWidth'] = nativeWidth;
-                        this.dataDoc['icon-nativeHeight'] = nativeHeight;
+                        this.dataDoc['icon_nativeWidth'] = nativeWidth;
+                        this.dataDoc['icon_nativeHeight'] = nativeHeight;
                     }, 500);
                 }
             );
@@ -201,13 +201,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(
-            () => this.rootDoc.scrollTop,
+            () => this.rootDoc.layout_scrollTop,
             () => {
-                if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '-panY'])) instanceof ComputedField)) {
-                    this.props.Document[this.SidebarKey + '-panY'] = ComputedField.MakeFunction('this.scrollTop');
+                if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
+                    this.props.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop');
                 }
-                this.props.Document[this.SidebarKey + '-viewScale'] = 1;
-                this.props.Document[this.SidebarKey + '-panX'] = 0;
+                this.props.Document[this.SidebarKey + '_freeform_scale'] = 1;
+                this.props.Document[this.SidebarKey + '_freeform_panX'] = 0;
             }
         );
     }
@@ -215,7 +215,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view);
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
-        if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(false);
             return true;
         }
@@ -239,7 +239,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         const docAnchor = () => {
             const anchor = Docs.Create.TextanchorDocument({
-                title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
+                title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)),
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
@@ -258,11 +258,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     loaded = (nw: number, nh: number, np: number) => {
-        this.dataDoc[this.props.fieldKey + '-numPages'] = np;
+        this.dataDoc[this.props.fieldKey + '_numPages'] = np;
         Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), (nw * 96) / 72));
         Doc.SetNativeHeight(this.dataDoc, (nh * 96) / 72);
         this.layoutDoc._height = this.layoutDoc[WidthSym]() / (Doc.NativeAspect(this.dataDoc) || 1);
-        !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
+        !this.Document._layout_fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
     };
 
     public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => {
@@ -279,14 +279,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public prevAnnotation = () => this._pdfViewer?.prevAnnotation();
     public nextAnnotation = () => this._pdfViewer?.nextAnnotation();
     public backPage = () => {
-        this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1);
+        this.Document._layout_curPage = Math.max(1, (NumCast(this.Document._layout_curPage) || 1) - 1);
         return true;
     };
     public forwardPage = () => {
-        this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '-numPages']), (NumCast(this.Document._curPage) || 1) + 1);
+        this.Document._layout_curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '_numPages']), (NumCast(this.Document._layout_curPage) || 1) + 1);
         return true;
     };
-    public gotoPage = (p: number) => (this.Document._curPage = p);
+    public gotoPage = (p: number) => (this.Document._layout_curPage = p);
 
     @undoBatch
     onKeyDown = action((e: KeyboardEvent) => {
@@ -316,9 +316,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
 
     // adding external documents; to sidebar key
-    // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"-annotation")
+    // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"_annotation")
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => {
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._show_sidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
     };
     sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => {
@@ -332,13 +332,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     .ScreenToLocalTransform()
                     .scale(this.props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
-                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
                 const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                    this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
                 }
                 return false;
             },
@@ -352,7 +352,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
@@ -363,7 +363,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         } else {
             this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
             this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
-            this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+            this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth;
         }
     });
     settingsPanel() {
@@ -378,7 +378,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </>
         );
         const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`;
-        const curPage = NumCast(this.Document._curPage) || 1;
+        const curPage = NumCast(this.Document._layout_curPage) || 1;
         return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
             <div
                 className="pdfBox-ui"
@@ -424,7 +424,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <input
                         value={curPage}
                         style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: 'all' }}
-                        onChange={e => (this.Document._curPage = Number(e.currentTarget.value))}
+                        onChange={e => (this.Document._layout_curPage = Number(e.currentTarget.value))}
                         onKeyDown={e => e.stopPropagation()}
                         onClick={action(() => (this._pageControls = !this._pageControls))}
                     />
@@ -470,7 +470,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._show_sidebar ? true : false;
     }
     @computed get sidebarHandle() {
         return (
@@ -480,7 +480,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={e => this.sidebarBtnDown(e, true)}>
@@ -490,10 +490,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     @computed get pdfScale() {
-        const pdfNativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+        const pdfNativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth, pdfNativeWidth);
         const pdfRatio = pdfNativeWidth / nativeWidth;
         return (pdfRatio * this.props.PanelWidth()) / pdfNativeWidth;
@@ -572,7 +572,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 onContextMenu={this.specificContextMenu}
                 style={{
                     display: this.props.thumbShown?.() ? 'none' : undefined,
-                    height: this.props.Document._scrollTop && !this.Document._fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
+                    height: this.props.Document._layout_scrollTop && !this.Document._layout_fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
                 }}>
                 <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
                 <div
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index f406ffbea..80b12b96e 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -41,7 +41,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
     setResult = (info: Upload.AccessPathInfo, presentation?: Presentation) => {
         this.result = info;
         this.dataDoc.type = DocumentType.VID;
-        this.dataDoc[this.fieldKey + '-duration'] = this.videoDuration;
+        this.dataDoc[this.fieldKey + '_duration'] = this.videoDuration;
 
         this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey);
         this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.client);
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 75e61bbeb..7bf765042 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -129,7 +129,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         }
     }
     getAnchor = (addAsAnnotation: boolean) => {
-        const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
+        const startTime = Cast(this.layoutDoc._layout_currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
         return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc;
     };
 
@@ -236,13 +236,13 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                 console.log('screenshotbox: upload');
                 const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
                 const [{ result }] = await Networking.UploadFilesToServer(file);
-                this.dataDoc[this.fieldKey + '-duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
+                this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
                 if (!(result instanceof Error)) {
                     // convert this screenshotBox into normal videoBox
                     this.dataDoc.type = DocumentType.VID;
                     this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
                     this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
-                    this.layoutDoc._fitWidth = undefined;
+                    this.layoutDoc._layout_fitWidth = undefined;
                     this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
                 } else alert('video conversion failed');
             };
@@ -270,14 +270,14 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     setupDictation = () => {
         if (this.dataDoc[this.fieldKey + '-dictation']) return;
         const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-        dictationText._autoHeight = false;
+        dictationText._layout_autoHeight = false;
         const dictationTextProto = Doc.GetProto(dictationText);
         dictationTextProto.recordingSource = this.dataDoc;
         dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
         dictationTextProto.mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState');
         this.dataDoc[this.fieldKey + '-dictation'] = dictationText;
     };
-    videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth();
+    videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth();
     formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
     render() {
         TraceMobx();
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index f09962b22..37fda14fc 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -167,7 +167,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
     // only included in buttons, transforms scripting UI to a button
     @action
     onFinish = () => {
-        this.rootDoc.layoutKey = 'layout';
+        this.rootDoc.layout_fieldKey = 'layout';
     };
 
     // displays error message
@@ -702,7 +702,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
     // toolbar (with compile and apply buttons) for scripting UI
     renderScriptingTools() {
-        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? '-finish' : '');
+        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
         return (
             <div className="scriptingBox-toolbar">
                 <button
@@ -730,7 +730,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
                     Save
                 </button>
 
-                {!StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout
+                {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout
                     <button
                         className={buttonStyle}
                         onPointerDown={e => {
@@ -776,7 +776,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
     // toolbar (with edit and run buttons and error message) for params UI
     renderTools(toolSet: string, func: () => void) {
-        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? '-finish' : '');
+        const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : '');
         return (
             <div className="scriptingBox-toolbar">
                 <button
@@ -795,7 +795,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
                     }}>
                     {toolSet}
                 </button>
-                {!StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? null : (
+                {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : (
                     <button
                         className={buttonStyle}
                         onPointerDown={e => {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 7a7d4fe37..e00cb8618 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import { basename } from 'path';
-import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { Doc, HeightSym, StrListCast, WidthSym } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
@@ -91,9 +91,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return LinkManager.Links(this.dataDoc);
     }
     @computed get heightPercent() {
-        return NumCast(this.layoutDoc._timelineHeightPercent, 100);
+        return NumCast(this.layoutDoc._layout_timelineHeightPercent, 100);
     } // current percent of video relative to VideoBox height
-    // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
+    // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "_duration"]); }
     @observable rawDuration: number = 0;
 
     @computed get youtubeVideoId() {
@@ -325,14 +325,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 y: NumCast(this.layoutDoc.y, 1),
                 _width: 150,
                 _height: 50,
-                title: (this.layoutDoc._currentTimecode || 0).toString(),
+                title: (this.layoutDoc._layout_currentTimecode || 0).toString(),
                 onClick: FollowLinkScript(),
             });
             this.props.addDocument?.(b);
-            DocUtils.MakeLink(b, this.rootDoc, { linkRelationship: 'video snapshot' });
+            DocUtils.MakeLink(b, this.rootDoc, { link_relationship: 'video snapshot' });
             Networking.PostToServer('/youtubeScreenshot', {
                 id: this.youtubeVideoId,
-                timecode: this.layoutDoc._currentTimecode,
+                timecode: this.layoutDoc._layout_currentTimecode,
             }).then(response => {
                 const resolved = response?.accessPaths?.agnostic?.client;
                 if (resolved) {
@@ -345,7 +345,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
             // if you want to preview the captured image,
             const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
-            const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, '_'));
+            const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._layout_currentTimecode || 0).toString().replace(/\./, '_'));
             const filename = basename(encodedFilename);
             Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY));
         }
@@ -354,8 +354,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     updateIcon = () => {
         const makeIcon = (returnedfilename: string) => {
             this.dataDoc.icon = new ImageField(returnedfilename);
-            this.dataDoc['icon-nativeWidth'] = this.layoutDoc[WidthSym]();
-            this.dataDoc['icon-nativeHeight'] = this.layoutDoc[HeightSym]();
+            this.dataDoc.icon_nativeWidth = this.layoutDoc[WidthSym]();
+            this.dataDoc.icon_nativeHeight = this.layoutDoc[HeightSym]();
         };
         this.Snapshot(undefined, undefined, makeIcon);
     };
@@ -373,18 +373,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             onClick: FollowLinkScript(),
             _width: 150,
             _height: (height / width) * 150,
-            title: '--snapshot' + NumCast(this.layoutDoc._currentTimecode) + ' image-',
+            title: '--snapshot' + NumCast(this.layoutDoc._layout_currentTimecode) + ' image-',
         });
         Doc.SetNativeWidth(Doc.GetProto(imageSnapshot), Doc.NativeWidth(this.layoutDoc));
         Doc.SetNativeHeight(Doc.GetProto(imageSnapshot), Doc.NativeHeight(this.layoutDoc));
         this.props.addDocument?.(imageSnapshot);
-        const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { linkRelationship: 'video snapshot' });
-        link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
+        const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' });
+        link && (Doc.GetProto(link.link_anchor_2 as Doc).timecodeToHide = NumCast((link.link_anchor_2 as Doc).timecodeToShow) + 3);
         setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true));
     };
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
-        const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
+        const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
         const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
         if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
         const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc;
@@ -402,16 +402,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         }
         if (Number.isFinite(this.player!.duration)) {
             this.rawDuration = this.player!.duration;
-            this.dataDoc[this.fieldKey + '-duration'] = this.rawDuration;
-        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '-duration']);
+            this.dataDoc[this.fieldKey + '_duration'] = this.rawDuration;
+        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '_duration']);
     });
 
     // updates video time
     @action
     updateTimecode = () => {
-        this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
+        this.player && (this.layoutDoc._layout_currentTimecode = this.player.currentTime);
         try {
-            this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
+            this._youtubePlayer && (this.layoutDoc._layout_currentTimecode = this._youtubePlayer.getCurrentTime?.());
         } catch (e) {
             console.log('Video Timecode Exception:', e);
         }
@@ -423,8 +423,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // extracts video thumbnails and saves them as field of doc
     getVideoThumbnails = () => {
-        if (this.dataDoc.thumbnails !== undefined) return;
-        this.dataDoc.thumbnails = new List<string>();
+        if (this.dataDoc[this.fieldKey + '_thumbnails'] !== undefined) return;
+        this.dataDoc[this.fieldKey + '_thumbnails'] = new List<string>();
         const thumbnailPromises: Promise<any>[] = [];
         const video = document.createElement('video');
 
@@ -442,7 +442,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             if (newTime < video.duration) {
                 video.currentTime = newTime;
             } else {
-                Promise.all(thumbnailPromises).then(thumbnails => (this.dataDoc.thumbnails = new List<string>(thumbnails)));
+                Promise.all(thumbnailPromises).then(thumbnails => (this.dataDoc[this.fieldKey + '_thumbnails'] = new List<string>(thumbnails)));
             }
         };
 
@@ -460,12 +460,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
             this._disposers.reactionDisposer?.();
             this._disposers.reactionDisposer = reaction(
-                () => NumCast(this.layoutDoc._currentTimecode),
+                () => NumCast(this.layoutDoc._layout_currentTimecode),
                 time => !this._playing && (vref.currentTime = time),
                 { fireImmediately: true }
             );
 
-            (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
+            (!this.dataDoc[this.fieldKey + '_thumbnails'] || this.dataDoc[this.fieldKey + '_thumbnails'].length != VideoBox.numThumbnails) && this.getVideoThumbnails();
         }
     };
 
@@ -531,7 +531,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
                         // delete assoicated video data
                         this.dataDoc[this.props.fieldKey] = '';
-                        this.dataDoc[this.fieldKey + '-duration'] = '';
+                        this.dataDoc[this.fieldKey + '_duration'] = '';
                         // delete assoicated presentation data
                         this.dataDoc[this.fieldKey + '-presentation'] = '';
                     },
@@ -573,7 +573,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         key="video"
                         autoPlay={this._screenCapture}
                         ref={this.setVideoRef}
-                        style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.rootDoc._viewScale)})`, transformOrigin: 'top left' } : {}}
+                        style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.rootDoc._freeform_scale)})`, transformOrigin: 'top left' } : {}}
                         onCanPlay={this.videoLoad}
                         controls={VideoBox._nativeControls}
                         onPlay={() => this.Play()}
@@ -620,8 +620,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._disposers.reactionDisposer?.();
             this._disposers.youtubeReactionDisposer?.();
             this._disposers.reactionDisposer = reaction(
-                () => this.layoutDoc._currentTimecode,
-                () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode))
+                () => this.layoutDoc._layout_currentTimecode,
+                () => !this._playing && this.Seek(NumCast(this.layoutDoc._layout_currentTimecode))
             );
             this._disposers.youtubeReactionDisposer = reaction(
                 () => Doc.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
@@ -677,15 +677,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this._clicking = false;
                 if (this.props.isContentActive()) {
                     // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
-                    // this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
+                    // this.layoutDoc._layout_timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
 
-                    this.layoutDoc._timelineHeightPercent = 80;
+                    this.layoutDoc._layout_timelineHeightPercent = 80;
                 }
                 return false;
             }),
             emptyFunction,
             () => {
-                this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
+                this.layoutDoc._layout_timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
                 setTimeout(
                     action(() => (this._clicking = false)),
                     500
@@ -721,7 +721,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
         const classname = 'videoBox-content-YouTube' + (this._fullScreen ? '-fullScreen' : '');
-        const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
+        const start = untracked(() => Math.round(NumCast(this.layoutDoc._layout_currentTimecode)));
         return (
             <iframe
                 key={this._youtubeIframeId}
@@ -740,7 +740,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @action.bound
     addDocWithTimecode(doc: Doc | Doc[]): boolean {
         const docs = doc instanceof Doc ? [doc] : doc;
-        const curTime = NumCast(this.layoutDoc._currentTimecode);
+        const curTime = NumCast(this.layoutDoc._layout_currentTimecode);
         docs.forEach(doc => (doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1));
         return this.addDocument(doc);
     }
@@ -859,7 +859,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // starts marquee selection
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._viewScale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -890,7 +890,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             .scale(this.scaling())
             .translate(0, (-this.heightPercent / 100) * this.props.PanelHeight());
 
-    setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._currentTimecode = time);
+    setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._layout_currentTimecode = time);
 
     timelineHeight = () => (this.props.PanelHeight() * (100 - this.heightPercent)) / 100;
 
@@ -899,7 +899,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     scaling = () => this.props.NativeDimScaling?.() || 1;
 
     panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100;
-    panelHeight = () => (this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
+    panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
 
     screenToLocalTransform = () => {
         const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
@@ -956,9 +956,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 <CollectionStackedTimeline
                     ref={action((r: any) => (this._stackedTimeline = r))}
                     {...this.props}
+                    dataFieldKey={this.fieldKey}
                     fieldKey={this.annotationKey}
                     dictationKey={this.fieldKey + '-dictation'}
                     mediaPath={this.audiopath}
+                    thumbnails={() => StrListCast(this.dataDoc[this.fieldKey + '_thumbnails'])}
                     renderDepth={this.props.renderDepth + 1}
                     startTag={'_timecodeToShow' /* videoStart */}
                     endTag={'_timecodeToHide' /* videoEnd */}
@@ -1003,7 +1005,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const anchy = NumCast(cropping.y);
         const anchw = NumCast(cropping._width);
         const anchh = NumCast(cropping._height);
-        const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw;
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
@@ -1014,25 +1016,25 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.onClick = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
-        croppingProto.isPrototype = true;
+        croppingProto.isDataDoc = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.VID;
         croppingProto.layout = VideoBox.LayoutString('data');
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
-        croppingProto['data-nativeWidth'] = anchw;
-        croppingProto['data-nativeHeight'] = anchh;
+        croppingProto['data_nativeWidth'] = anchw;
+        croppingProto['data_nativeHeight'] = anchh;
         croppingProto.videoCrop = true;
-        croppingProto.currentTimecode = this.layoutDoc._currentTimecode;
-        croppingProto.viewScale = viewScale;
-        croppingProto.viewScaleMin = viewScale;
-        croppingProto.panX = anchx / viewScale;
-        croppingProto.panY = anchy / viewScale;
-        croppingProto.panXMin = anchx / viewScale;
-        croppingProto.panXMax = anchw / viewScale;
-        croppingProto.panYMin = anchy / viewScale;
-        croppingProto.panYMax = anchh / viewScale;
+        croppingProto.layout_currentTimecode = this.layoutDoc._layout_currentTimecode;
+        croppingProto.freeform_scale = viewScale;
+        croppingProto.freeform_scaleMin = viewScale;
+        croppingProto.freeform_ = anchx / viewScale;
+        croppingProto.freeform_panY = anchy / viewScale;
+        croppingProto.freeform_panXMin = anchx / viewScale;
+        croppingProto.freeform_panXMax = anchw / viewScale;
+        croppingProto.freeform_panYMin = anchy / viewScale;
+        croppingProto.freeform_panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
+            DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
         }
         this.props.bringToFront(cropping);
         return cropping;
@@ -1050,7 +1052,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 style={{
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
                     borderRadius,
-                    overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? 'auto' : undefined,
+                    overflow: this.props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined,
                 }}
                 onWheel={e => {
                     e.stopPropagation();
@@ -1116,7 +1118,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @computed get UIButtons() {
         const bounds = this.props.docViewPath().lastElement().getBounds();
         const width = (bounds?.right || 0) - (bounds?.left || 0);
-        const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
+        const curTime = NumCast(this.layoutDoc._layout_currentTimecode) - (this.timeline?.clipStart || 0);
         return (
             <>
                 <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 82c8e796d..3da5d8f17 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -56,7 +56,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _keyInput = React.createRef<HTMLInputElement>();
-    private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop));
+    private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.layout_scrollTop));
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
     private _searchString = '';
@@ -101,7 +101,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             ImageCast(
                 this.layoutDoc['thumb-frozen'],
                 ImageCast(
-                    this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) && this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight)
+                    this.layoutDoc.thumbScrollTop === this.layoutDoc._layout_scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) && this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight)
                         ? this.layoutDoc.thumb
                         : undefined
                 )
@@ -144,7 +144,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     updateThumb = async () => {
         if (!this._iframe) return;
-        const scrollTop = NumCast(this.layoutDoc._scrollTop);
+        const scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
         const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
         var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
@@ -181,12 +181,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
-            this._annotationKeySuffix = () => this._urlHash + '-annotations';
+            this._annotationKeySuffix = () => this._urlHash + '_annotations';
             const reqdFuncs: { [key: string]: string } = {};
-            // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
-            reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"])`;
-            reqdFuncs[this.fieldKey + '-annotations-setter'] = `this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"] = value`;
-            reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"])`;
+            // bcz: need to make sure that doc.data_annotations points to the currently active web page's annotations (this could/should be when the doc is created)
+            reqdFuncs[this.fieldKey + '_annotations'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"])`;
+            reqdFuncs[this.fieldKey + '_annotations-setter'] = `this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"] = value`;
+            reqdFuncs[this.fieldKey + '_sidebar'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_sidebar"])`;
             DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
         this._disposers.urlchange = reaction(
@@ -196,12 +196,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         );
 
-        this._disposers.autoHeight = reaction(
-            () => this.layoutDoc._autoHeight,
-            autoHeight => {
-                if (autoHeight) {
-                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
+        this._disposers.layout_autoHeight = reaction(
+            () => this.layoutDoc._layout_autoHeight,
+            layout_autoHeight => {
+                if (layout_autoHeight) {
+                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -225,7 +225,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
 
         this._disposers.scrollReaction = reaction(
-            () => NumCast(this.layoutDoc._scrollTop),
+            () => NumCast(this.layoutDoc._layout_scrollTop),
             scrollTop => {
                 const viewTrans = StrCast(this.Document._viewTransition);
                 const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
@@ -281,7 +281,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     focus = (anchor: Doc, options: DocFocusOptions) => {
         if (anchor !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight));
+            const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight));
             if (scrollTo !== undefined) {
                 if (this._initialScroll === undefined) {
                     const focusTime = options.zoomTime ?? 500;
@@ -296,14 +296,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     getView = (doc: Doc) => {
-        if (this.rootDoc.layoutKey === 'layout_icon') this.props.DocumentView?.().iconify();
+        if (this.rootDoc.layout_fieldKey === 'layout_icon') this.props.DocumentView?.().iconify();
         if (this._url && WebCast(doc.presData).url.href !== this._url) this.setData(WebCast(doc.presData).url.href);
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
     };
 
     sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
-        if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(false);
             return true;
         }
@@ -321,8 +321,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchor =
             this._getAnchor(this._savedAnnotations, false) ??
             Docs.Create.WebanchorDocument({
-                title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
-                y: NumCast(this.layoutDoc._scrollTop),
+                title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._layout_scrollTop),
+                y: NumCast(this.layoutDoc._layout_scrollTop),
                 unrendered: true,
                 annotationOn: this.rootDoc,
             });
@@ -349,9 +349,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._selectionText = sel.toString();
                 AnchorMenu.Instance.setSelectedText(sel.toString());
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
-                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
+                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
                 // Changing which document to add the annotation to (the currently selected WebBox)
-                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}-${this._urlHash}-sidebar`);
+                GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash}_sidebar`);
                 GPTPopup.Instance.addDoc = this.sidebarAddDocument;
             }
         }
@@ -364,7 +364,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-        e.button !== 2 && (this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale]);
+        e.button !== 2 && (this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale]);
         if (word || ((e.target as any) || '').className.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
             setTimeout(
                 action(() => (this._marqueeing = undefined)),
@@ -485,7 +485,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 batch.end();
                             });
                             if (this._outerRef.current) {
-                                this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
+                                this._outerRef.current.scrollTop = NumCast(this.layoutDoc._layout_scrollTop);
                                 this._outerRef.current.scrollLeft = 0;
                             }
                         }
@@ -508,8 +508,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (e.ctrlKey) {
             if (this._innerCollectionView) {
                 this._innerCollectionView.zoom(e.screenX, e.screenY, e.deltaY);
-                const offset = e.clientY - NumCast(this.layoutDoc._scrollTop);
-                this.layoutDoc.panY = offset - offset / NumCast(this.layoutDoc._viewScale) + NumCast(this.layoutDoc._scrollTop) - NumCast(this.layoutDoc._scrollTop) / NumCast(this.layoutDoc._viewScale);
+                const offset = e.clientY - NumCast(this.layoutDoc._layout_scrollTop);
+                this.layoutDoc.panY = offset - offset / NumCast(this.layoutDoc._freeform_scale) + NumCast(this.layoutDoc._layout_scrollTop) - NumCast(this.layoutDoc._layout_scrollTop) / NumCast(this.layoutDoc._freeform_scale);
             }
             e.preventDefault();
         }
@@ -532,7 +532,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.layoutDoc.thumbScrollTop = undefined;
                 this.layoutDoc.thumbNativeWidth = undefined;
                 this.layoutDoc.thumbNativeHeight = undefined;
-                this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
+                this.layoutDoc.layout_scrollTop = this._outerRef.current.scrollTop = newScrollTop;
             } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
         }, timeout);
     };
@@ -550,13 +550,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     forward = (checkAvailable?: boolean) => {
-        const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'), []);
-        const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []);
+        const future = Cast(this.rootDoc[this.fieldKey + '_future'], listSpec('string'), []);
+        const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
         if (checkAvailable) return future.length;
         runInAction(() => {
             if (future.length) {
                 const curUrl = this._url;
-                this.dataDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
+                this.dataDoc[this.fieldKey + '_history'] = new List<string>([...history, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
                 this._scrollHeight = 0;
                 if (this._webUrl === this._url) {
@@ -572,14 +572,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     back = (checkAvailable?: boolean) => {
-        const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'));
-        const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []);
+        const future = Cast(this.rootDoc[this.fieldKey + '_future'], listSpec('string'));
+        const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
         if (checkAvailable) return history.length;
         runInAction(() => {
             if (history.length) {
                 const curUrl = this._url;
-                if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
-                else this.dataDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
+                if (future === undefined) this.dataDoc[this.fieldKey + '_future'] = new List<string>([this._url]);
+                else this.dataDoc[this.fieldKey + '_future'] = new List<string>([...future, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
                 this._scrollHeight = 0;
                 if (this._webUrl === this._url) {
@@ -643,10 +643,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (Field.toString(data) === this._url) return false;
         this._scrollHeight = 0;
         const oldUrl = this._url;
-        const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []);
+        const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []);
         const weburl = new WebField(Field.toString(data));
-        this.dataDoc[this.fieldKey + '-future'] = new List<string>([]);
-        this.dataDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), oldUrl]);
+        this.dataDoc[this.fieldKey + '_future'] = new List<string>([]);
+        this.dataDoc[this.fieldKey + '_history'] = new List<string>([...(history || []), oldUrl]);
         this.dataDoc[this.fieldKey] = weburl;
         return true;
     };
@@ -659,7 +659,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
         if (!cm.findByDescription('Options...')) {
-            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : 'Use') + ' Cors', event: () => (this.layoutDoc.useCors = !this.layoutDoc.useCors), icon: 'snowflake' });
+            !Doc.noviceMode &&
+                funcs.push({ description: (this.layoutDoc[this.fieldKey + '_useCors'] ? "Don't Use" : 'Use') + ' Cors', event: () => (this.layoutDoc[this.fieldKey + '_useCors'] = !this.layoutDoc[this.fieldKey + '_useCors']), icon: 'snowflake' });
             funcs.push({
                 description: (this.layoutDoc.allowScripts ? 'Prevent' : 'Allow') + ' Scripts',
                 event: () => {
@@ -672,12 +673,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 icon: 'snowflake',
             });
             funcs.push({
-                description: (!this.layoutDoc.forceReflow ? 'Force' : 'Prevent') + ' Reflow',
+                description: (!this.layoutDoc.layout_forceReflow ? 'Force' : 'Prevent') + ' Reflow',
                 event: () => {
-                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
-                    this.layoutDoc.forceReflow = !nw;
+                    const nw = !this.layoutDoc.layout_forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
+                    this.layoutDoc.layout_forceReflow = !nw;
                     if (nw) {
-                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + '-nativeWidth', nw, true);
+                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + '_nativeWidth', nw, true);
                     }
                 },
                 icon: 'snowflake',
@@ -726,7 +727,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setTimeout(
             action(() => {
                 if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
-                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.layout_scrollTop)));
                 }
                 this._webPageHasBeenRendered = true;
             })
@@ -736,7 +737,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             return <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
         }
         if (field instanceof WebField) {
-            const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl;
+            const url = this.layoutDoc[this.fieldKey + '_useCors'] ? Utils.CorsProxy(this._webUrl) : this._webUrl;
             return (
                 <iframe
                     className="webBox-iframe"
@@ -758,7 +759,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         return this.addDocumentWrapper(doc, sidebarKey);
     };
     @observable _draggingSidebar = false;
@@ -774,15 +775,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     .ScreenToLocalTransform()
                     .scale(this.props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
-                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
-                const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight']);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
+                const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight']);
                 const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
                 const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.NativeDimScaling?.() || 1)) / nativeWidth;
                 if (ratio >= 1) {
                     this.layoutDoc.nativeWidth = nativeWidth * ratio;
                     this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
                     onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                    this.layoutDoc._layout_showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
                 }
                 return false;
             }),
@@ -804,7 +805,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title="Toggle Sidebar"
                 style={{
                     display: !this.props.isContentActive() ? 'none' : undefined,
-                    top: StrCast(this.layoutDoc._showTitle) === 'title' ? 20 : 5,
+                    top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
                 onPointerDown={e => this.sidebarBtnDown(e, true)}>
@@ -815,12 +816,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         if (!nativeWidth) {
             const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym]();
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
             Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth);
-            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']);
         }
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
@@ -830,10 +831,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
         } else {
-            this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar;
+            this.layoutDoc._layout_showSidebar = !this.layoutDoc._layout_showSidebar;
             this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
-            if (!this.layoutDoc._showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
-                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '-nativeWidth'] = undefined;
+            if (!this.layoutDoc._layout_showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
+                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '_nativeWidth'] = undefined;
             } else {
                 this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
             }
@@ -862,8 +863,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 className={'webBox-cont' + (interactive ? '-interactive' : '')}
                 onKeyDown={e => e.stopPropagation()}
                 style={{
-                    width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%',
-                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.panX)}px, ${-NumCast(this.layoutDoc.panY)}px)`,
+                    width: !this.layoutDoc.layout_forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']) || `100%` : '100%',
+                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`,
                 }}>
                 {this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc) ? null : this.urlContent}
             </div>
@@ -876,7 +877,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             <div
                 className="webBox-annotationLayer"
                 style={{
-                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.panX)}px, ${-NumCast(this.layoutDoc.panY)}px)`,
+                    transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`,
                     height: Doc.NativeHeight(this.Document) || undefined,
                 }}
                 ref={this._annotationLayer}>
@@ -889,7 +890,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
     }
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     @computed get webpage() {
@@ -914,7 +915,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 ScreenToLocalTransform={this.scrollXf}
                 NativeDimScaling={returnOne}
                 focus={this.focus}
-                dropAction={'alias'}
+                dropAction="embed"
                 docFilters={docFilters}
                 select={emptyFunction}
                 isAnyChildContentActive={returnFalse}
@@ -989,7 +990,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
@@ -1055,7 +1056,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         ref={this._sidebarRef}
                         {...this.props}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                        fieldKey={this.fieldKey + '-' + this._urlHash}
+                        fieldKey={this.fieldKey + '_' + this._urlHash}
                         rootDoc={this.rootDoc}
                         layoutDoc={this.layoutDoc}
                         dataDoc={this.dataDoc}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index a4968dcd0..5e615f2c1 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -44,16 +44,12 @@ export enum ButtonType {
     ToggleButton = 'tglBtn',
     ColorButton = 'colorBtn',
     ToolButton = 'toolBtn',
-    NumberButton = 'numBtn',
+    NumberSliderButton = 'numSliderBtn',
+    NumberDropdownButton = 'numDropdownBtn',
+    NumberInlineButton = 'numInlineBtn',
     EditableText = 'editableText',
 }
 
-export enum NumButtonType {
-    Slider = 'slider',
-    DropdownOptions = 'list',
-    Inline = 'inline',
-}
-
 export interface ButtonProps extends FieldViewProps {
     type?: ButtonType;
 }
@@ -132,8 +128,52 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     /**
      * Number button
      */
-    @computed get numberButton() {
-        const numBtnType: string = StrCast(this.rootDoc.numBtnType);
+    @computed get numberSliderButton() {
+        const numScript = ScriptCast(this.rootDoc.script);
+        const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value');
+
+        // Script for checking the outcome of the toggle
+        const checkResult = Number(numScript?.script.run({ self: this.rootDoc, value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
+
+        const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
+
+        const dropdown = (
+            <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}>
+                <input
+                    type="range"
+                    step="1"
+                    min={NumCast(this.rootDoc.numBtnMin, 0)}
+                    max={NumCast(this.rootDoc.numBtnMax, 100)}
+                    value={checkResult}
+                    className="menu-slider"
+                    onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
+                    onPointerUp={() => this._batch?.end()}
+                    onChange={e => {
+                        e.stopPropagation();
+                        setValue(Number(e.target.value));
+                    }}
+                />
+            </div>
+        );
+        return (
+            <div
+                className="menuButton numBtn slider"
+                onPointerDown={e => e.stopPropagation()}
+                onClick={action(() => {
+                    this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
+                    this.noTooltip = this.rootDoc.dropDownOpen;
+                    Doc.UnBrushAllDocs();
+                })}>
+                {checkResult}
+                {label}
+                {this.rootDoc.dropDownOpen ? dropdown : null}
+            </div>
+        );
+    }
+    /**
+     * Number button
+     */
+    @computed get numberDropdownButton() {
         const numScript = ScriptCast(this.rootDoc.script);
         const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value');
 
@@ -142,102 +182,71 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
 
-        if (numBtnType === NumButtonType.Slider) {
-            const dropdown = (
-                <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}>
-                    <input
-                        type="range"
-                        step="1"
-                        min={NumCast(this.rootDoc.numBtnMin, 0)}
-                        max={NumCast(this.rootDoc.numBtnMax, 100)}
-                        value={checkResult}
-                        className="menu-slider"
-                        onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
-                        onPointerUp={() => this._batch?.end()}
-                        onChange={e => {
-                            e.stopPropagation();
-                            setValue(Number(e.target.value));
-                        }}
-                    />
+        const items: number[] = [];
+        for (let i = 0; i < 100; i++) {
+            if (i % 2 === 0) {
+                items.push(i);
+            }
+        }
+        const list = items.map(value => {
+            return (
+                <div
+                    className="list-item"
+                    key={`${value}`}
+                    style={{
+                        backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined,
+                    }}
+                    onClick={() => setValue(value)}>
+                    {value}
                 </div>
             );
-            return (
+        });
+        return (
+            <div className="menuButton numBtn list">
+                <div className="button" onClick={action(e => setValue(Number(checkResult) - 1))}>
+                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'minus'} />
+                </div>
                 <div
-                    className={`menuButton ${this.type} ${numBtnType}`}
-                    onPointerDown={e => e.stopPropagation()}
+                    className={`button ${'number'}`}
+                    onPointerDown={e => {
+                        e.stopPropagation();
+                        e.preventDefault();
+                    }}
                     onClick={action(() => {
                         this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
                         this.noTooltip = this.rootDoc.dropDownOpen;
                         Doc.UnBrushAllDocs();
                     })}>
-                    {checkResult}
-                    {label}
-                    {this.rootDoc.dropDownOpen ? dropdown : null}
+                    <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={undoBatch(action(e => setValue(Number(e.target.value))))} />
+                </div>
+                <div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}>
+                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
                 </div>
-            );
-        } else if (numBtnType === NumButtonType.DropdownOptions) {
-            const items: number[] = [];
-            for (let i = 0; i < 100; i++) {
-                if (i % 2 === 0) {
-                    items.push(i);
-                }
-            }
-            const list = items.map(value => {
-                return (
-                    <div
-                        className="list-item"
-                        key={`${value}`}
-                        style={{
-                            backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined,
-                        }}
-                        onClick={() => setValue(value)}>
-                        {value}
-                    </div>
-                );
-            });
-            return (
-                <div className={`menuButton ${this.type} ${numBtnType}`}>
-                    <div className={`button`} onClick={action(e => setValue(Number(checkResult) - 1))}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'minus'} />
-                    </div>
-                    <div
-                        className={`button ${'number'}`}
-                        onPointerDown={e => {
-                            e.stopPropagation();
-                            e.preventDefault();
-                        }}
-                        onClick={action(() => {
-                            this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
-                            this.noTooltip = this.rootDoc.dropDownOpen;
-                            Doc.UnBrushAllDocs();
-                        })}>
-                        <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={undoBatch(action(e => setValue(Number(e.target.value))))} />
-                    </div>
-                    <div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
-                    </div>
 
-                    {this.rootDoc.dropDownOpen ? (
-                        <div>
-                            <div className="menuButton-dropdownList" style={{ left: '25%' }}>
-                                {list}
-                            </div>
-                            <div
-                                className="dropbox-background"
-                                onClick={action(e => {
-                                    e.stopPropagation();
-                                    this.rootDoc.dropDownOpen = false;
-                                    this.noTooltip = false;
-                                    Doc.UnBrushAllDocs();
-                                })}
-                            />
+                {this.rootDoc.dropDownOpen ? (
+                    <div>
+                        <div className="menuButton-dropdownList" style={{ left: '25%' }}>
+                            {list}
                         </div>
-                    ) : null}
-                </div>
-            );
-        } else {
-            return <div />;
-        }
+                        <div
+                            className="dropbox-background"
+                            onClick={action(e => {
+                                e.stopPropagation();
+                                this.rootDoc.dropDownOpen = false;
+                                this.noTooltip = false;
+                                Doc.UnBrushAllDocs();
+                            })}
+                        />
+                    </div>
+                ) : null}
+            </div>
+        );
+    }
+    /**
+     * Number button
+     */
+    @computed get numberInlineButton() {
+        return <div />;
     }
 
     /**
@@ -528,7 +537,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             case ButtonType.EditableText: return this.editableText;
             case ButtonType.DropdownList: button = this.dropdownListButton; break;
             case ButtonType.ColorButton: button = this.colorButton; break;
-            case ButtonType.NumberButton: button = this.numberButton; break;
+            case ButtonType.NumberDropdownButton: button = this.numberDropdownButton; break;
+            case ButtonType.NumberInlineButton: button = this.numberInlineButton; break;
+            case ButtonType.NumberSliderButton: button = this.numberSliderButton; break;
             case ButtonType.DropdownButton: button = this.dropdownButton; break;
             case ButtonType.ToggleButton: button = this.toggleButton; break;
             case ButtonType.TextButton: 
@@ -613,7 +624,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
     }
     Doc.SharingDoc().userColor = undefined;
     Doc.GetProto(Doc.SharingDoc()).userColor = color;
-    Doc.UserDoc().showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().showTitle, 'creationDate');
+    Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'creationDate');
 });
 
 // toggle: Set overlay status of selected document
@@ -626,29 +637,29 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
 });
 
-ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snapline' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
+ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     // prettier-ignore
-    const map: Map<'grid' | 'snapline' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+    const map: Map<'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
         ['grid', {
             undo: false,
-            checkResult: (doc:Doc) => doc._backgroundGridShow,
-            setDoc: (doc:Doc) => doc._backgroundGridShow = !doc._backgroundGridShow,
+            checkResult: (doc:Doc) => doc._freeform_backgroundGrid,
+            setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
         }],
-        ['snapline', {
+        ['snaplines', {
             undo: false,
-            checkResult: (doc:Doc) => doc.showSnapLines,
-            setDoc: (doc:Doc) => doc._showSnapLines = !doc._showSnapLines,
+            checkResult: (doc:Doc) => doc._freeform_snapLines,
+            setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines,
         }],
         ['viewAll', {
             undo: false,
-            checkResult: (doc:Doc) => doc._fitContentsToBox,
-            setDoc: (doc:Doc) => doc._fitContentsToBox = !doc._fitContentsToBox,
+            checkResult: (doc:Doc) => doc._freeform_fitContentsToBox,
+            setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
         }],
         ['clusters', {
             undo: false,
-            checkResult: (doc:Doc) => doc._useClusters,
-            setDoc: (doc:Doc) => doc._useClusters = !doc._useClusters,
+            checkResult: (doc:Doc) => doc._freeform_useClusters,
+            setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters,
         }],
         ['arrange', {
             undo: true,
@@ -850,8 +861,8 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
     // prettier-ignore
     const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
         ['inkMask', {
-            checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'),
-            setInk: (doc: Doc) => (doc.isInkMask = !doc.isInkMask),
+            checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.stroke_isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'),
+            setInk: (doc: Doc) => (doc.stroke_isInkMask = !doc.stroke_isInkMask),
             setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
         }],
         ['fillColor', {
@@ -860,8 +871,8 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
             setMode: () => SetActiveFillColor(StrCast(value)),
         }],
         [ 'strokeWidth', {
-            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.strokeWidth) : ActiveInkWidth()),
-            setInk: (doc: Doc) => (doc.strokeWidth = NumCast(value)),
+            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.stroke_width) : ActiveInkWidth()),
+            setInk: (doc: Doc) => (doc.stroke_width = NumCast(value)),
             setMode: () => SetActiveInkWidth(value.toString()),
         }],
         ['strokeColor', {
@@ -914,24 +925,24 @@ ScriptingGlobals.add(function webBack(checkResult?: boolean) {
 ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult && selected) {
-        const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
+        const result: boolean = NumCast(selected.schema_previewWidth) > 0;
         if (result) return Colors.MEDIUM_BLUE;
         else return 'transparent';
     } else if (selected) {
-        if (NumCast(selected.schemaPreviewWidth) > 0) {
-            selected.schemaPreviewWidth = 0;
+        if (NumCast(selected.schema_previewWidth) > 0) {
+            selected.schema_previewWidth = 0;
         } else {
-            selected.schemaPreviewWidth = 200;
+            selected.schema_previewWidth = 200;
         }
     }
 });
 ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult && selected) {
-        return NumCast(selected._singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
+        return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
     }
     if (selected) {
-        selected._singleLine = !selected._singleLine;
+        selected._schema_singleLine = !selected._schema_singleLine;
     }
 });
 
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index c05a30d1a..c929b7ff3 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -41,18 +41,33 @@ export class DashDocView {
 
         this.root = ReactDOM.createRoot(this.dom);
         this.root.render(
-            <DashDocViewInternal docId={node.attrs.docId} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />
+            <DashDocViewInternal
+                docId={node.attrs.docId}
+                embedding={node.attrs.embedding}
+                width={node.attrs.width}
+                height={node.attrs.height}
+                hidden={node.attrs.hidden}
+                fieldKey={node.attrs.fieldKey}
+                tbox={tbox}
+                view={view}
+                node={node}
+                getPos={getPos}
+            />
         );
     }
     destroy() {
-        setTimeout(this.root.unmount);
+        setTimeout(() => {
+            try {
+                this.root.unmount();
+            } catch {}
+        });
     }
     selectNode() {}
 }
 
 interface IDashDocViewInternal {
     docId: string;
-    alias: string;
+    embedding: string;
     tbox: FormattedTextBox;
     width: string;
     height: string;
@@ -69,25 +84,18 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
     _textBox: FormattedTextBox;
     @observable _dashDoc: Doc | undefined;
     @observable _finalLayout: any;
-    @observable _resolvedDataDoc: any;
     @observable _width: number = 0;
     @observable _height: number = 0;
 
     updateDoc = action((dashDoc: Doc) => {
         this._dashDoc = dashDoc;
-        this._finalLayout = this.props.docId ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc);
+        this._finalLayout = dashDoc;
 
-        if (this._finalLayout) {
-            if (!Doc.AreProtosEqual(this._finalLayout, dashDoc)) {
-                this._finalLayout.rootDocument = dashDoc.aliasOf;
-            }
-            this._resolvedDataDoc = Cast(this._finalLayout.resolvedDataDoc, Doc, null);
-        }
         if (this.props.width !== (this._dashDoc?._width ?? '') + 'px' || this.props.height !== (this._dashDoc?._height ?? '') + 'px') {
             try {
                 this._width = NumCast(this._dashDoc?._width);
                 this._height = NumCast(this._dashDoc?._height);
-                // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
+                // bcz: an exception will be thrown if two embeddings are open at the same time when a doc view comment is made
                 this.props.view.dispatch(
                     this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
                         ...this.props.node.attrs,
@@ -105,15 +113,15 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
         super(props);
         this._textBox = this.props.tbox;
 
-        DocServer.GetRefField(this.props.docId + this.props.alias).then(async dashDoc => {
+        DocServer.GetRefField(this.props.docId + this.props.embedding).then(async dashDoc => {
             if (!(dashDoc instanceof Doc)) {
-                this.props.alias &&
+                this.props.embedding &&
                     DocServer.GetRefField(this.props.docId).then(async dashDocBase => {
                         if (dashDocBase instanceof Doc) {
-                            const aliasedDoc = Doc.MakeAlias(dashDocBase, this.props.docId + this.props.alias);
-                            aliasedDoc.layoutKey = 'layout';
-                            this.props.fieldKey && DocUtils.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, this.props.fieldKey, undefined);
-                            this.updateDoc(aliasedDoc);
+                            const embedding = Doc.MakeEmbedding(dashDocBase, this.props.docId + this.props.embedding);
+                            embedding.layout_fieldKey = 'layout';
+                            this.props.fieldKey && DocUtils.makeCustomViewClicked(embedding, Docs.Create.StackingDocument, this.props.fieldKey, undefined);
+                            this.updateDoc(embedding);
                         }
                     });
             } else {
@@ -191,7 +199,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                 onWheel={e => e.preventDefault()}>
                 <DocumentView
                     Document={this._finalLayout}
-                    DataDoc={this._resolvedDataDoc}
                     addDocument={returnFalse}
                     rootSelected={returnFalse} //{this._textBox.props.isSelected}
                     removeDocument={this.removeDoc}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index a9e59ab1c..6c61f6709 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -143,16 +143,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement();
         if (container) {
-            const alias = Doc.MakeAlias(container.props.Document);
-            alias._viewType = CollectionViewType.Time;
-            let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
+            const embedding = Doc.MakeEmbedding(container.rootDoc);
+            embedding._viewType = CollectionViewType.Time;
+            const colHdrKey = '_' + container.LayoutFieldKey + '_columnHeaders';
+            let list = Cast(embedding[colHdrKey], listSpec(SchemaHeaderField));
             if (!list) {
-                alias._columnHeaders = list = new List<SchemaHeaderField>();
+                embedding[colHdrKey] = list = new List<SchemaHeaderField>();
             }
             list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
             list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
-            alias._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
-            this.props.tbox.props.addDocTab(alias, OpenWhere.addRight);
+            embedding._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey;
+            this.props.tbox.props.addDocTab(embedding, OpenWhere.addRight);
         }
     };
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 88adba1e7..023814a9d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -118,38 +118,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return this._editorView;
     }
     public get SidebarKey() {
-        return this.fieldKey + '-sidebar';
+        return this.fieldKey + '_sidebar';
     }
     @computed get allSidebarDocs() {
         return DocListCast(this.dataDoc[this.SidebarKey]);
     }
 
     @computed get noSidebar() {
-        return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._noSidebar;
+        return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._layout_noSidebar;
     }
-    @computed get sidebarWidthPercent() {
-        return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    @computed get layout_sidebarWidthPercent() {
+        return this._showSidebar ? '20%' : StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
     }
     @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.fieldKey + '-backgroundColor'], '#e4e4e4'));
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
-    @computed get autoHeight() {
-        return (this.props.forceAutoHeight || this.layoutDoc._autoHeight) && !this.props.ignoreAutoHeight;
+    @computed get layout_autoHeight() {
+        return (this.props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this.props.ignoreAutoHeight;
     }
     @computed get textHeight() {
-        return NumCast(this.rootDoc[this.fieldKey + '-height']);
+        return NumCast(this.rootDoc[this.fieldKey + '_height']);
     }
     @computed get scrollHeight() {
-        return NumCast(this.rootDoc[this.fieldKey + '-scrollHeight']);
+        return NumCast(this.rootDoc[this.fieldKey + '_scrollHeight']);
     }
     @computed get sidebarHeight() {
-        return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '-height']);
+        return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '_height']);
     }
     @computed get titleHeight() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
     }
-    @computed get autoHeightMargins() {
-        return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins);
+    @computed get layout_autoHeightMargins() {
+        return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins);
     }
     @computed get _recording() {
         return this.dataDoc?.mediaState === 'recording';
@@ -213,8 +213,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     public RemoveLinkFromDoc(linkDoc?: Doc) {
         this.unhighlightSearchTerms();
         const state = this._editorView?.state;
-        const a1 = linkDoc?.anchor1 as Doc;
-        const a2 = linkDoc?.anchor2 as Doc;
+        const a1 = linkDoc?.link_anchor_1 as Doc;
+        const a2 = linkDoc?.link_anchor_2 as Doc;
         if (state && a1 && a2 && this._editorView) {
             this.removeDocument(a1);
             this.removeDocument(a2);
@@ -255,18 +255,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         AnchorMenu.Instance.Status = 'marquee';
 
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => {
-            !this.layoutDoc.showSidebar && this.toggleSidebar();
+            !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
             setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
         };
         AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
-            !this.layoutDoc.showSidebar && this.toggleSidebar();
+            !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
             const anchor = this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true, true);
             setTimeout(() => {
                 const target = this._sidebarRef.current?.anchorMenuClick(anchor);
                 if (target) {
                     anchor.followLinkAudio = true;
                     DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
-                    target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
+                    target.title = ComputedField.MakeFunction(`self["text_audioAnnotations_text"].lastElement()`);
                 }
             });
         };
@@ -323,7 +323,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
                     this._applyingChange = this.fieldKey;
                     const textChange = curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text;
-                    textChange && (dataDoc[this.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())));
+                    textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
                     if ((!curTemp && !curProto) || curText || json.includes('dash')) {
                         // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
                         if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
@@ -333,7 +333,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             } else {
                                 dataDoc[this.fieldKey] = new RichTextField(json, curText);
                             }
-                            dataDoc[this.fieldKey + '-noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+                            dataDoc[this.fieldKey + '_noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
                             textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
                             unchanged = false;
                         }
@@ -341,7 +341,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         // if we've deleted all the text in a note driven by a template, then restore the template data
                         dataDoc[this.fieldKey] = undefined;
                         this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data)));
-                        dataDoc[this.fieldKey + '-noTemplate'] = undefined; // mark the data field as not being split from any template it might have
+                        dataDoc[this.fieldKey + '_noTemplate'] = undefined; // mark the data field as not being split from any template it might have
                         ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
                         unchanged = false;
                     }
@@ -371,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         let linkAnchor;
         let link;
         LinkManager.Links(this.dataDoc).forEach((l, i) => {
-            const anchor = (l.anchor1 as Doc).annotationOn ? (l.anchor1 as Doc) : (l.anchor2 as Doc).annotationOn ? (l.anchor2 as Doc) : undefined;
+            const anchor = (l.link_anchor_1 as Doc).annotationOn ? (l.link_anchor_1 as Doc) : (l.link_anchor_2 as Doc).annotationOn ? (l.link_anchor_2 as Doc) : undefined;
             if (anchor && (anchor.annotationOn as Doc).mediaState === 'recording') {
                 linkTime = NumCast(anchor._timecodeToShow /* audioStart */);
                 linkAnchor = anchor;
@@ -407,7 +407,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     autoLink = () => {
         const newAutoLinks = new Set<Doc>();
-        const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.linkRelationship === LinkManager.AutoKeywords);
+        const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords);
         if (this._editorView?.state.doc.textContent) {
             const isNodeSel = this._editorView.state.selection instanceof NodeSelection;
             const f = this._editorView.state.selection.from;
@@ -419,7 +419,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
         }
-        oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
+        oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
     };
 
     updateTitle = () => {
@@ -428,7 +428,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             !this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
             (title.startsWith('-') || title.startsWith('@')) &&
             this._editorView &&
-            !this.dataDoc['title-custom'] &&
+            !this.dataDoc.title_custom &&
             (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === 'text')
         ) {
             let node = this._editorView.state.doc;
@@ -460,8 +460,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
                             alink =
                                 alink ??
-                                (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) ||
-                                    DocUtils.MakeLink(this.props.Document, target, { linkRelationship: LinkManager.AutoKeywords })!);
+                                (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)) ||
+                                    DocUtils.MakeLink(this.props.Document, target, { link_relationship: LinkManager.AutoKeywords })!);
                             newAutoLinks.add(alink);
                             const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
                             allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
@@ -528,7 +528,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             this.setupEditor(this.config, this.fieldKey);
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc);
         }
-        // if (this.autoHeight) this.tryUpdateScrollHeight();
+        // if (this.layout_autoHeight) this.tryUpdateScrollHeight();
     };
 
     @undoBatch
@@ -554,11 +554,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     docId: target[Id],
                     float: 'unset',
                 });
-                if (!['alias', 'copy'].includes((dragData.dropAction ?? '') as any)) {
+                if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) {
                     dragData.removeDocument?.(dragData.draggedDocuments[0]);
                 }
-                target._fitContentsToBox = true;
-                target.context = this.rootDoc;
+                target._freeform_fitContentsToBox = true;
+                target.embedContainer = this.rootDoc;
                 const view = this._editorView!;
                 view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
                 e.stopPropagation();
@@ -655,14 +655,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     @observable _showSidebar = false;
     @computed get SidebarShown() {
-        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
     @action
     toggleSidebar = (preview: boolean = false) => {
         const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
         if (preview) this._showSidebar = true;
-        else this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
+        else this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
 
         this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
     };
@@ -685,11 +685,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             .ScreenToLocalTransform()
             .scale(this.props.NativeDimScaling?.() || 1)
             .transformDirection(delta[0], delta[1]);
-        const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.sidebarWidthPercent.replace('%', ''))) / 100;
+        const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
         const width = this.layoutDoc[WidthSym]() + localDelta[0];
-        this.layoutDoc._sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
+        this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
         this.layoutDoc.width = width;
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
     };
@@ -757,7 +757,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             description: 'plain',
             event: undoBatch(() => {
                 Doc.setNativeView(this.rootDoc);
-                this.layoutDoc.autoHeightMargins = undefined;
+                this.layoutDoc.layout_autoHeightMargins = undefined;
             }),
             icon: 'eye',
         });
@@ -765,18 +765,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             description: 'metadata',
             event: undoBatch(() => {
                 this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
-                this.rootDoc.layoutKey = 'layout_meta';
-                setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50), 50);
+                this.rootDoc.layout_fieldKey = 'layout_meta';
+                setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._layout_autoHeightMargins = 50), 50);
             }),
             icon: 'eye',
         });
-        const noteTypesDoc = Cast(Doc.UserDoc()['template-notes'], Doc, null);
+        const noteTypesDoc = Cast(Doc.UserDoc().template_notes, Doc, null);
         DocListCast(noteTypesDoc?.data).forEach(note => {
             const icon: IconProp = StrCast(note.icon) as IconProp;
             changeItems.push({
                 description: StrCast(note.title),
                 event: undoBatch(() => {
-                    this.layoutDoc.autoHeightMargins = undefined;
+                    this.layoutDoc.layout_autoHeightMargins = undefined;
                     Doc.setNativeView(this.rootDoc);
                     DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
                 }),
@@ -802,11 +802,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         );
 
         const uicontrols: ContextMenuProps[] = [];
-        uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: !this.Document._noSidebar ? 'eye-slash' : 'eye' });
         uicontrols.push({
-            description: (this.Document._showAltContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
-            event: () => (this.layoutDoc._showAltContentUI = !this.layoutDoc._showAltContentUI),
-            icon: !this.Document._showAltContentUI ? 'eye-slash' : 'eye',
+            description: !this.Document._layout_noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle',
+            event: () => (this.layoutDoc._layout_noSidebar = !this.layoutDoc._layout_noSidebar),
+            icon: !this.Document._layout_noSidebar ? 'eye-slash' : 'eye',
+        });
+        uicontrols.push({
+            description: (this.Document._layout_altContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
+            event: () => (this.layoutDoc._layout_altContentUI = !this.layoutDoc._layout_altContentUI),
+            icon: !this.Document._layout_altContentUI ? 'eye-slash' : 'eye',
         });
         uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
         !Doc.noviceMode &&
@@ -837,10 +841,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
                         this.rootDoc.isTemplateDoc = false;
                         this.rootDoc.isTemplateForField = '';
-                        this.rootDoc.layoutKey = 'layout';
+                        this.rootDoc.layout_fieldKey = 'layout';
                         MakeTemplate(this.rootDoc, true, title);
                         setTimeout(() => {
-                            this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height
+                            this.rootDoc._layout_autoHeight = this.layoutDoc._layout_autoHeight; // layout_autoHeight, width and height
                             this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
                             this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root.  This should probably apply to all '_' fields
                             this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, 'string', null);
@@ -848,7 +852,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         }, 10);
                     }
                     Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
-                    Doc.AddDocToList(Cast(Doc.UserDoc()['template-notes'], Doc, null), 'data', this.rootDoc);
+                    Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.rootDoc);
                 },
                 icon: 'eye',
             });
@@ -859,11 +863,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
         optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
         optionItems.push({
-            description: !this.Document._singleLine ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
-            event: () => (this.layoutDoc._singleLine = !this.layoutDoc._singleLine),
-            icon: !this.Document._singleLine ? 'grip-lines' : 'bars',
+            description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
+            event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
+            icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
         });
-        !Doc.noviceMode && optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: this.Document._autoHeight ? 'lock' : 'unlock' });
+        !Doc.noviceMode &&
+            optionItems.push({
+                description: `${this.Document._layout_autoHeight ? 'Lock' : 'Auto'} Height`,
+                event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight),
+                icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
+            });
         optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' });
         !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
         this._downX = this._downY = Number.NaN;
@@ -903,8 +912,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     y: NumCast(this.rootDoc.y),
                     _height: 200,
                     _width: 200,
-                    'data-nativeWidth': result.nativeWidth,
-                    'data-nativeHeight': result.nativeHeight,
+                    data_nativeWidth: result.nativeWidth,
+                    data_nativeHeight: result.nativeHeight,
                 });
                 if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc)) {
                     newDoc.overlayX = this.rootDoc.x;
@@ -914,7 +923,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     this.props.addDocument?.(newDoc);
                 }
                 // Create link between prompt and image
-                DocUtils.MakeLink(this.rootDoc, newDoc, { linkRelationship: 'Image Prompt' });
+                DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
             }
         } catch (err) {
             console.log(err);
@@ -957,8 +966,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement();
                 if (link) {
                     Doc.GetProto(link).isDictation = true;
-                    const audioanchor = Cast(link.anchor2, Doc, null);
-                    const textanchor = Cast(link.anchor1, Doc, null);
+                    const audioanchor = Cast(link.link_anchor_2, Doc, null);
+                    const textanchor = Cast(link.link_anchor_1, Doc, null);
                     if (audioanchor) {
                         audioanchor.backgroundColor = 'tan';
                         const audiotag = this._editorView.state.schema.nodes.audiotag.create({
@@ -1082,11 +1091,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     };
 
-    // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
+    // if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
     // Since we also monitor all component height changes, this will update the document's height.
     resetNativeHeight = (scrollHeight: number) => {
         const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight);
-        this.rootDoc[this.fieldKey + '-height'] = scrollHeight;
+        this.rootDoc[this.fieldKey + '_height'] = scrollHeight;
         if (nh) this.layoutDoc._nativeHeight = scrollHeight;
     };
 
@@ -1097,9 +1106,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this._cachedLinks = LinkManager.Links(this.Document);
         this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation);
-        this._disposers.autoHeight = reaction(
-            () => this.autoHeight,
-            autoHeight => autoHeight && this.tryUpdateScrollHeight()
+        this._disposers.layout_autoHeight = reaction(
+            () => this.layout_autoHeight,
+            layout_autoHeight => layout_autoHeight && this.tryUpdateScrollHeight()
         );
         this._disposers.highlights = reaction(
             () => Array.from(FormattedTextBox._globalHighlights).slice(),
@@ -1111,16 +1120,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             width => this.tryUpdateScrollHeight()
         );
         this._disposers.scrollHeight = reaction(
-            () => ({ scrollHeight: this.scrollHeight, autoHeight: this.autoHeight, width: NumCast(this.layoutDoc._width) }),
-            ({ width, scrollHeight, autoHeight }) => width && autoHeight && this.resetNativeHeight(scrollHeight),
+            () => ({ scrollHeight: this.scrollHeight, layout_autoHeight: this.layout_autoHeight, width: NumCast(this.layoutDoc._width) }),
+            ({ width, scrollHeight, layout_autoHeight }) => width && layout_autoHeight && this.resetNativeHeight(scrollHeight),
             { fireImmediately: true }
         );
         this._disposers.componentHeights = reaction(
-            // set the document height when one of the component heights changes and autoHeight is on
-            () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }),
-            ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
+            // set the document height when one of the component heights changes and layout_autoHeight is on
+            () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layout_autoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }),
+            ({ sidebarHeight, textHeight, layout_autoHeight, marginsHeight }) => {
                 const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
-                if (autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) {
+                if (layout_autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) {
                     this.props.setHeight?.(newHeight);
                 }
             },
@@ -1145,7 +1154,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._disposers.editorState = reaction(
             () => {
                 const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc?.proto), this.fieldKey) ? DocCast(this.layoutDoc?.proto) : this?.dataDoc;
-                const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : dataDoc?.[this.fieldKey + '-noTemplate'] || !this.layoutDoc[this.fieldKey] ? dataDoc : this.layoutDoc;
+                const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : dataDoc?.[this.fieldKey + '_noTemplate'] || !this.layoutDoc[this.fieldKey] ? dataDoc : this.layoutDoc;
                 return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(whichDoc[this.fieldKey]) };
             },
             incomingValue => {
@@ -1223,7 +1232,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
         var quickScroll: string | undefined = '';
         this._disposers.scroll = reaction(
-            () => NumCast(this.layoutDoc._scrollTop),
+            () => NumCast(this.layoutDoc._layout_scrollTop),
             pos => {
                 if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) {
                     const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
@@ -1300,7 +1309,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 }
             }, 0);
             dataDoc.title = exportState.title;
-            this.dataDoc['title-custom'] = true;
+            this.dataDoc.title_custom = true;
             dataDoc.googleDocUnchanged = true;
         } else {
             delete dataDoc[GoogleRef];
@@ -1357,7 +1366,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             view.state.schema.marks.linkAnchor.create({
                                 allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }],
                                 location: 'add:right',
-                                title: `from: ${DocCast(pdfAnchor.context).title}`,
+                                title: `from: ${DocCast(pdfAnchor.embedContainer).title}`,
                                 noPreview: true,
                                 docref: false,
                             }),
@@ -1366,7 +1375,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         ]),
                     ]);
 
-                    const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { linkRelationship: 'PDF pasted' });
+                    const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { link_relationship: 'PDF pasted' });
                     if (link) {
                         view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
                     }
@@ -1454,7 +1463,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (startupText) {
                     dispatch(state.tr.insertText(startupText));
                 }
-                const textAlign = StrCast(this.dataDoc['text-align'], StrCast(Doc.UserDoc().textAlign, 'left'));
+                const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign, 'left'));
                 if (textAlign !== 'left') {
                     selectAll(this._editorView.state, tr => {
                         this._editorView!.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
@@ -1527,7 +1536,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         } else (e.nativeEvent as any).handledByInnerReactInstance = true;
 
         if (this.Document.forceActive) e.stopPropagation();
-        this.tryUpdateScrollHeight(); // if a doc a fitWidth doc is being viewed in different context (eg freeform & lightbox), then it will have conflicting heights.  so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view.
+        this.tryUpdateScrollHeight(); // if a doc a fitWidth doc is being viewed in different embedContainer (eg freeform & lightbox), then it will have conflicting heights.  so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view.
         if ((e.target as any).tagName === 'AUDIOTAG') {
             e.preventDefault();
             e.stopPropagation();
@@ -1730,7 +1739,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     setTimeout(
                         () =>
                             translateGoogleApi(result1[0], { from: 'es', to: 'en' }).then((result: any) => {
-                                this.dataDoc[this.fieldKey + '-translation'] = result1 + '\r\n\r\n' + result[0];
+                                this.dataDoc[this.fieldKey + '_translation'] = result1 + '\r\n\r\n' + result[0];
                             }),
                         1000
                     );
@@ -1740,10 +1749,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             }
             this._lastText = curText;
         }
-        if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc['title-custom']) {
+        if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc.title_custom) {
             UndoManager.RunInBatch(() => {
-                this.dataDoc['title-custom'] = true;
-                this.dataDoc.showTitle = 'title';
+                this.dataDoc.title_custom = true;
+                this.dataDoc.layout_showTitle = 'title';
                 const tr = this._editorView!.state.tr;
                 this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.rootDoc.title).length + 2))).deleteSelection());
             }, 'titler');
@@ -1803,7 +1812,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (!LinkDocPreview.LinkInfo && this._scrollRef.current) {
             if (!this.props.dontSelectOnLoad) {
                 this._ignoreScroll = true;
-                this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop;
+                this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop;
                 this._ignoreScroll = false;
                 e.stopPropagation();
                 e.preventDefault();
@@ -1823,7 +1832,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
             if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) {
                 // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
-                const setScrollHeight = () => (this.rootDoc[this.fieldKey + '-scrollHeight'] = scrollHeight);
+                const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
                 if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
                     setScrollHeight();
                 } else {
@@ -1832,21 +1841,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             }
         }
     };
-    fitContentsToBox = () => BoolCast(this.props.Document._fitContentsToBox);
-    sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+    fitContentsToBox = () => BoolCast(this.props.Document._freeform_fitContentsToBox);
+    sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => {
-        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
     };
     sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey);
     sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey);
-    setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '-height'] = height);
-    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '_height'] = height);
+    sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
     sidebarScreenToLocal = () =>
         this.props
             .ScreenToLocalTransform()
             .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0)
-            .scale(1 / NumCast(this.layoutDoc._viewScale, 1) / (this.props.NativeDimScaling?.() || 1));
+            .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this.props.NativeDimScaling?.() || 1));
 
     @computed get audioHandle() {
         return !this._recording ? null : (
@@ -1880,7 +1889,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     color,
                     opacity: annotated ? 1 : undefined,
                 }}>
-                <FontAwesomeIcon icon={'comment-alt'} />
+                <FontAwesomeIcon icon="comment-alt" />
             </div>
         );
     }
@@ -1932,25 +1941,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         fitContentsToBox={this.fitContentsToBox}
                         noSidebar={true}
                         treeViewHideTitle={true}
-                        fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}-translation` : `${this.fieldKey}-sidebar`}
+                        fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
                     />
                 </div>
             );
         };
         return (
-            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                 {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
             </div>
         );
     }
     cycleAlternateText = () => {
-        if (this.layoutDoc._showAltContentUI) {
-            const usePath = this.rootDoc[`${this.props.fieldKey}-usePath`];
-            this.rootDoc[`_${this.props.fieldKey}-usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
+        if (this.layoutDoc._layout_altContentUI) {
+            const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+            this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
         }
     };
     @computed get overlayAlternateIcon() {
-        const usePath = this.rootDoc[`${this.props.fieldKey}-usePath`];
+        const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
         return (
             <Tooltip
                 title={
@@ -1982,23 +1991,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         );
     }
     @computed get fieldKey() {
-        const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}-usePath`]);
-        return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `-${usePath.replace(':hover', '')}` : '');
+        const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}_usePath`]);
+        return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `_${usePath.replace(':hover', '')}` : '');
     }
     @observable _isHovering = false;
     render() {
         TraceMobx();
         const active = this.props.isContentActive() || this.props.isSelected();
         const selected = active;
-        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
         const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
         const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-        const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
-        const selPaddingClass = selected && !this.layoutDoc._singleLine && paddingY >= 10 ? '-selected' : '';
+        const selPad = (selected && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
+        const selPaddingClass = selected && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
         const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
         return styleFromLayoutString?.height === '0px' ? null : (
             <div
@@ -2010,7 +2019,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
                         (e: WheelEvent) => {
                             if (this.props.isContentActive() && !this.props.allowScroll) {
-                                if (!NumCast(this.layoutDoc._scrollTop) && e.deltaY <= 0) e.preventDefault();
+                                if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault();
                                 e.stopPropagation();
                             }
                         },
@@ -2028,7 +2037,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                           }),
                     display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
                     transition: 'inherit',
-                    // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
+                    // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
                     color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
                     fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
                     fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily),
@@ -2039,8 +2048,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     className="formattedTextBox-cont"
                     ref={this._ref}
                     style={{
-                        overflow: this.autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an autoHeight doc in its own tab, or in the lightbox
-                        height: this.props.height || (this.autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
+                        overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
+                        height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
                         pointerEvents: interactive ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
@@ -2056,9 +2065,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
                         ref={this._scrollRef}
                         style={{
-                            width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
+                            width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
                             pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
-                            overflow: this.layoutDoc._singleLine ? 'hidden' : this.layoutDoc._autoHeight ? 'visible' : undefined,
+                            overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
                         }}
                         onScroll={this.onScroll}
                         onDrop={this.ondrop}>
@@ -2075,10 +2084,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             }}
                         />
                     </div>
-                    {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
-                    {this.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
+                    {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+                    {this.noSidebar || this.Document._layout_noSidebar || this.props.dontSelectOnLoad || this.Document._createDocOnCR ? null : this.sidebarHandle}
                     {this.audioHandle}
-                    {this.layoutDoc._showAltContentUI ? this.overlayAlternateIcon : null}
+                    {this.layoutDoc._layout_altContentUI ? this.overlayAlternateIcon : null}
                 </div>
             </div>
         );
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index f0caa1f4f..5e0041b84 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -623,15 +623,15 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                     if (linkclicked) {
                         const linkDoc = await DocServer.GetRefField(linkclicked);
                         if (linkDoc instanceof Doc) {
-                            const anchor1 = await Cast(linkDoc.anchor1, Doc);
-                            const anchor2 = await Cast(linkDoc.anchor2, Doc);
+                            const link_anchor_1 = await Cast(linkDoc.link_anchor_1, Doc);
+                            const link_anchor_2 = await Cast(linkDoc.link_anchor_2, Doc);
                             const currentDoc = SelectionManager.Docs().lastElement();
-                            if (currentDoc && anchor1 && anchor2) {
-                                if (Doc.AreProtosEqual(currentDoc, anchor1)) {
-                                    return StrCast(anchor2.title);
+                            if (currentDoc && link_anchor_1 && link_anchor_2) {
+                                if (Doc.AreProtosEqual(currentDoc, link_anchor_1)) {
+                                    return StrCast(link_anchor_2.title);
                                 }
-                                if (Doc.AreProtosEqual(currentDoc, anchor2)) {
-                                    return StrCast(anchor1.title);
+                                if (Doc.AreProtosEqual(currentDoc, link_anchor_2)) {
+                                    return StrCast(link_anchor_1.title);
                                 }
                             }
                         }
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index cad56b14b..104aed058 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -81,9 +81,9 @@ export class RichTextRules {
                 textDoc.inlineTextCount = numInlines + 1;
                 const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to
                 const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
-                const textDocInline = Docs.Create.TextDocument('', { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _fitWidth: true, _autoHeight: true, _fontSize: '9px', title: 'inline comment' });
+                const textDocInline = Docs.Create.TextDocument('', { _layout_fieldKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _layout_fitWidth: true, _layout_autoHeight: true, _fontSize: '9px', title: 'inline comment' });
                 textDocInline.title = inlineFieldKey; // give the annotation its own title
-                textDocInline['title-custom'] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
+                textDocInline.title_custom = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
                 textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
                 textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
                 textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
@@ -249,7 +249,7 @@ export class RichTextRules {
                         this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
                     }
 
-                    DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { linkRelationship: 'portal to:portal from' });
+                    DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
 
                     const fstate = this.TextBox.EditorView?.state;
                     if (fstate && selection) {
@@ -299,28 +299,6 @@ export class RichTextRules {
                 return tr.setSelection(new NodeSelection(tr.doc.resolve(tr.selection.$from.pos - 1)));
             }),
 
-            // create an inline view of a document {{ <layoutKey> : <Doc> }}
-            // {{:Doc}} => show default view of document
-            // {{<layout>}} => show layout for this doc
-            // {{<layout> : Doc}} => show layout for another doc
-            new InputRule(new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_@\.\? \-0-9]+)?\}\}$/), (state, match, start, end) => {
-                const fieldKey = match[1] || '';
-                const fieldParam = match[2]?.replace('…', '...') || '';
-                const rawdocid = match[3]?.substring(1);
-                const docId = rawdocid ? (!rawdocid.includes('@') ? normalizeEmail(Doc.CurrentUserEmail) + '@' + rawdocid : rawdocid) : undefined;
-                if (!fieldKey && !docId) return state.tr;
-                docId &&
-                    DocServer.GetRefField(docId).then(docx => {
-                        if (!(docx instanceof Doc && docx)) {
-                            Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docId);
-                        }
-                    });
-                const node = (state.doc.resolve(start) as any).nodeAfter;
-                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: 'dashDoc', docId, fieldKey: fieldKey + fieldParam, float: 'unset', alias: Utils.GenerateGuid() });
-                const sm = state.storedMarks || undefined;
-                return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
-            }),
-
             // create an inline view of a tag stored under the '#' field
             new InputRule(new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/), (state, match, start, end) => {
                 const tag = match[1];
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 6c9d5d31a..f27fb18e2 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -247,7 +247,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             hidden: { default: false }, // whether dashComment node has toggle the dashDoc's display off
             fieldKey: { default: '' },
             docId: { default: '' },
-            alias: { default: '' },
+            embedding: { default: '' },
         },
         group: 'inline',
         draggable: false,
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index c22107c03..858d83b7a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -273,7 +273,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     progressivizedItems = (doc: Doc) => {
         const targetList = PresBox.targetRenderedDoc(doc);
         if (doc.presIndexed !== undefined && targetList) {
-            const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '-annotations']);
+            const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '_annotations']);
             return listItems.filter(doc => !doc.unrendered);
         }
     };
@@ -419,7 +419,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame;
         if (activeFrame !== undefined) {
             const transTime = NumCast(activeItem.presTransition, 500);
-            const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc);
+            const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).embedContainer) : DocCast(activeItem.presentationTargetDoc);
             const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
             if (context) {
                 const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
@@ -435,7 +435,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const setData = bestTargetView?.ComponentView?.setData;
             if (setData) setData(activeItem.presData);
             else Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
-            bestTarget[fkey + '-usePath'] = activeItem.presUsePath;
+            bestTarget[fkey + '_usePath'] = activeItem.presUsePath;
             setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
         }
         if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) {
@@ -449,14 +449,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presClipWidth !== undefined)) {
-            if (bestTarget._clipWidth !== activeItem.presClipWidth) {
-                bestTarget._clipWidth = activeItem.presClipWidth;
+            const fkey = '_' + Doc.LayoutFieldKey(bestTarget);
+            if (bestTarget[fkey + '_clipWidth'] !== activeItem.presClipWidth) {
+                bestTarget[fkey + '_clipWidth'] = activeItem.presClipWidth;
                 changed = true;
             }
         }
         if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) {
-            if (bestTarget._currentTimecode !== activeItem.presStartTime) {
-                bestTarget._currentTimecode = activeItem.presStartTime;
+            if (bestTarget._layout_currentTimecode !== activeItem.presStartTime) {
+                bestTarget._layout_currentTimecode = activeItem.presStartTime;
                 changed = true;
             }
         }
@@ -504,8 +505,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
 
         if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presViewScroll !== undefined)) {
-            if (bestTarget._scrollTop !== activeItem.presViewScroll) {
-                bestTarget._scrollTop = activeItem.presViewScroll;
+            if (bestTarget._layout_scrollTop !== activeItem.presViewScroll) {
+                bestTarget._layout_scrollTop = activeItem.presViewScroll;
                 changed = true;
                 const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
                 if (contentBounds) {
@@ -516,19 +517,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.presAnnotations !== undefined)) {
             const fkey = Doc.LayoutFieldKey(bestTarget);
-            const oldItems = DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered);
+            const oldItems = DocListCast(bestTarget[fkey + '_annotations']).filter(doc => doc.unrendered);
             const newItems = DocListCast(activeItem.presAnnotations).map(doc => {
                 doc.hidden = false;
                 return doc;
             });
-            const hiddenItems = DocListCast(bestTarget[fkey + '-annotations'])
+            const hiddenItems = DocListCast(bestTarget[fkey + '_annotations'])
                 .filter(doc => !doc.unrendered && !newItems.includes(doc))
                 .map(doc => {
                     doc.hidden = true;
                     return doc;
                 });
             const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
-            Doc.GetProto(bestTarget)[fkey + '-annotations'] = newList;
+            Doc.GetProto(bestTarget)[fkey + '_annotations'] = newList;
         }
         if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) {
             changed = true;
@@ -560,20 +561,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
             if (contentBounds) {
                 const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
-                bestTarget._panX = viewport.panX;
-                bestTarget._panY = viewport.panY;
+                bestTarget._freeform_panX = viewport.panX;
+                bestTarget._freeform_panY = viewport.panY;
                 const dv = DocumentManager.Instance.getDocumentView(bestTarget);
                 if (dv) {
                     changed = true;
                     const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height);
-                    activeItem.presMovement === PresMovement.Zoom && (bestTarget._viewScale = computedScale);
+                    activeItem.presMovement === PresMovement.Zoom && (bestTarget._freeform_scale = computedScale);
                     dv.ComponentView?.brushView?.(viewport);
                 }
             } else {
-                if (bestTarget._panX !== activeItem.presPanX || bestTarget._panY !== activeItem.presPanY || bestTarget._viewScale !== activeItem.presViewScale) {
-                    bestTarget._panX = activeItem.presPanX ?? bestTarget._panX;
-                    bestTarget._panY = activeItem.presPanY ?? bestTarget._panY;
-                    bestTarget._viewScale = activeItem.presViewScale ?? bestTarget._viewScale;
+                if (bestTarget._freeform_panX !== activeItem.presPanX || bestTarget._freeform_panY !== activeItem.presPanY || bestTarget._freeform_scale !== activeItem.presViewScale) {
+                    bestTarget._freeform_panX = activeItem.presPanX ?? bestTarget._freeform_panX;
+                    bestTarget._freeform_panY = activeItem.presPanY ?? bestTarget._freeform_panY;
+                    bestTarget._freeform_scale = activeItem.presViewScale ?? bestTarget._freeform_scale;
                     changed = true;
                 }
             }
@@ -610,12 +611,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinProps?.activeFrame !== undefined;
             const fkey = Doc.LayoutFieldKey(targetDoc);
             if (pinProps.pinData.dataview) {
-                pinDoc.presUsePath = targetDoc[fkey + '-usePath'];
+                pinDoc.presUsePath = targetDoc[fkey + '_usePath'];
                 pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
             }
             if (pinProps.pinData.dataannos) {
                 const fkey = Doc.LayoutFieldKey(targetDoc);
-                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered));
+                pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '_annotations']).filter(doc => !doc.unrendered));
             }
             if (pinProps.pinData.inkable) {
                 pinDoc.presFillColor = targetDoc.fillColor;
@@ -623,8 +624,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 pinDoc.presWidth = targetDoc._width;
                 pinDoc.presHeight = targetDoc._height;
             }
-            if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._scrollTop;
-            if (pinProps.pinData.clippable) pinDoc.presClipWidth = targetDoc._clipWidth;
+            if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._layout_scrollTop;
+            if (pinProps.pinData.clippable) {
+                const fkey = Doc.LayoutFieldKey(targetDoc);
+                pinDoc.presClipWidth = targetDoc[fkey + '_clipWidth'];
+            }
             if (pinProps.pinData.datarange) {
                 pinDoc.presXRange = undefined; //targetDoc?.xrange;
                 pinDoc.presYRange = undefined; //targetDoc?.yrange;
@@ -649,13 +653,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField);
             if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField;
             if (pinProps.pinData.pannable) {
-                pinDoc.presPanX = NumCast(targetDoc._panX);
-                pinDoc.presPanY = NumCast(targetDoc._panY);
-                pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1);
+                pinDoc.presPanX = NumCast(targetDoc._freeform_panX);
+                pinDoc.presPanY = NumCast(targetDoc._freeform_panY);
+                pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
             }
             if (pinProps.pinData.temporal) {
-                pinDoc.presStartTime = targetDoc._currentTimecode;
-                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(targetDoc.presStartTime) + 0.1);
+                pinDoc.presStartTime = targetDoc._layout_currentTimecode;
+                const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.presStartTime) + 0.1);
                 pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration);
             }
         }
@@ -663,7 +667,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // If pinWithView option set then update scale and x / y props of slide
             const bounds = pinProps.pinViewport;
             pinDoc.presPinView = true;
-            pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1);
+            pinDoc.presViewScale = NumCast(targetDoc._freeform_scale, 1);
             pinDoc.presPanX = bounds.left + bounds.width / 2;
             pinDoc.presPanY = bounds.top + bounds.height / 2;
             pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
@@ -793,7 +797,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const savedStates = docs.map(doc => {
             switch (doc.type) {
                 case DocumentType.COL:
-                    if (doc._viewType === CollectionViewType.Freeform) return { type: CollectionViewType.Freeform, doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) };
+                    if (doc._viewType === CollectionViewType.Freeform) return { type: CollectionViewType.Freeform, doc, x: NumCast(doc.freeform_panX), y: NumCast(doc.freeform_panY), s: NumCast(doc.freeform_scale) };
                     break;
                 case DocumentType.INK:
                     if (doc.data instanceof InkField) {
@@ -813,7 +817,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 const { x, y, s, doc } = savedState!;
                                 doc._panX = x;
                                 doc._panY = y;
-                                doc._viewScale = s;
+                                doc._freeform_scale = s;
                             }
                             break;
                         case DocumentType.INK:
@@ -1008,17 +1012,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     return false;
                 }
             } else {
-                if (!doc.aliasOf) {
-                    const original = Doc.MakeAlias(doc);
-                    TabDocView.PinDoc(original, {});
-                    setTimeout(() => this.removeDocument(doc), 0);
-                    return false;
-                } else {
-                    if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
-                    doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
-                    doc.presMovement = PresMovement.Zoom;
-                    if (this._expandBoolean) doc.presExpandInlineButton = true;
-                }
+                if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
+                doc.presentationTargetDoc = doc.createdFrom; // dropped document will be a new embedding of an embedded document somewhere else.
+                doc.presMovement = PresMovement.Zoom;
+                if (this._expandBoolean) doc.presExpandInlineButton = true;
             }
         });
         return true;
@@ -1086,7 +1083,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 this.rootDoc._itemIndex = index;
             }
         } else this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
-        this.updateCurrentPresentation(DocCast(doc.context));
+        this.updateCurrentPresentation(DocCast(doc.embedContainer));
     };
 
     //Command click
@@ -1240,7 +1237,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
             .forEach((doc, index) => {
                 const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
-                const srcContext = Cast(tagDoc.context, Doc, null);
+                const srcContext = Cast(tagDoc.embedContainer, Doc, null);
                 const width = NumCast(tagDoc._width) / 10;
                 const height = Math.max(NumCast(tagDoc._height) / 10, 15);
                 const edge = Math.max(width, height);
@@ -1300,7 +1297,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Method called for viewing paths which adds a single line with
      * points at the center of each document added.
-     * Design choice: When this is called it sets _fitContentsToBox as true so the
+     * Design choice: When this is called it sets _freeform_fitContentsToBox as true so the
      * user can have an overview of all of the documents in the collection.
      * (Design needed for when documents in presentation trail are in another
      * collection)
@@ -1553,7 +1550,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
                                 // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
                                 let dataField = Doc.LayoutFieldKey(tagDoc);
-                                if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '-annotations';
+                                if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
 
                                 if (DocCast(activeItem.presentationTargetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc.annotationOn["${dataField}"]`);
                                 else activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc["${dataField}"]`);
@@ -1743,7 +1740,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem = this.activeItem;
         if (activeItem && this.targetDoc) {
             const clipStart = NumCast(activeItem.clipStart);
-            const clipEnd = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '-duration']));
+            const clipEnd = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '_duration']));
             return (
                 <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                     <div>
@@ -2077,10 +2074,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // prettier-ignore
         switch (layout) {
             case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
-            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
-            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
-            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
-            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x, y })
+            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
+            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
+            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y });
+            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _layoutFitContentsToBox: true, x, y })
         }
     };
 
@@ -2458,7 +2455,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 childIgnoreNativeSize={true}
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
-                                //childFitWidth={returnTrue}
+                                //childLayoutFitWidth={returnTrue}
                                 childOpacity={returnOne}
                                 //childLayoutString={PresElementBox.LayoutString('data')}
                                 childClickScript={PresBox.navigateToDocScript}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 62e5314c3..e48d2d674 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -61,7 +61,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     componentDidMount() {
-        this.layoutDoc.hideLinkButton = true;
+        this.layoutDoc.layout_hideLinkButton = true;
         this._heightDisposer = reaction(
             () => ({ expand: this.rootDoc.presExpandInlineButton, height: this.collapsedHeight }),
             ({ expand, height }) => (this.layoutDoc._height = height + (expand ? this.expandViewHeight : 0)),
@@ -319,7 +319,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // a previously recorded video will have timecode defined
     static videoIsRecorded = (activeItem: Opt<Doc>) => {
         const casted = Cast(activeItem?.recording, Doc, null);
-        return casted && 'currentTimecode' in casted;
+        return casted && 'layout_currentTimecode' in casted;
     };
 
     removeAllRecordingInOverlay = () => {
@@ -380,10 +380,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 _width: 384,
                 _height: 216,
                 hideDocumentButtonBar: true,
-                hideDecorationTitle: true,
+                layout_hideDecorationTitle: true,
                 hideOpenButton: true,
                 // hideDeleteButton: true,
-                cloneFieldFilter: new List<string>(['system']),
+                cloneFieldFilter: new List<string>(['isSystem']),
             });
 
             // attach the recording to the slide, and attach the slide to the recording
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index cc0072a94..8bd060d4f 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -51,8 +51,8 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
         const newDoc = Docs.Create.TextDocument(this.props.text.trim(), {
             _width: 200,
             _height: 50,
-            _fitWidth: true,
-            _autoHeight: true,
+            _layout_fitWidth: true,
+            _layout_autoHeight: true,
         });
         this.addDoc(newDoc, this.sidebarId);
     };
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index ea709a859..40b745cc4 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -90,7 +90,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @observable isAnnotating = false;
     // key where data is stored
     @computed get allAnnotations() {
-        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']), this.props.docFilters(), this.props.docRangeFilters());
+        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.props.docFilters(), this.props.docRangeFilters());
     }
     @computed get inlineTextAnnotations() {
         return this.allAnnotations.filter(a => a.textInlineAnnotations);
@@ -101,12 +101,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.setupPdfJsViewer();
         this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0));
 
-        this._disposers.autoHeight = reaction(
-            () => this.props.layoutDoc._autoHeight,
-            autoHeight => {
-                if (autoHeight) {
-                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
+        this._disposers.layout_autoHeight = reaction(
+            () => this.props.layoutDoc._layout_autoHeight,
+            layout_autoHeight => {
+                if (layout_autoHeight) {
+                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '_nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -123,7 +123,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             { fireImmediately: true }
         );
         this._disposers.curPage = reaction(
-            () => Cast(this.props.Document._curPage, 'number', null),
+            () => Cast(this.props.Document._layout_curPage, 'number', null),
             page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
             { fireImmediately: true }
         );
@@ -181,14 +181,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
-            if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
+            const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
+            if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._layout_scrollTop) {
                 if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc };
                 else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper);
                 else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) });
             }
         } else {
-            this._initialScroll = { loc: NumCast(this.props.layoutDoc._scrollTop), easeFunc: options.easeFunc };
+            this._initialScroll = { loc: NumCast(this.props.layoutDoc._layout_scrollTop), easeFunc: options.easeFunc };
         }
         return focusSpeed;
     };
@@ -208,18 +208,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     pagesinit = () => {
         if (this._pdfViewer._setDocumentViewerElement?.offsetParent) {
-            runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1));
-            this.gotoPage(NumCast(this.props.Document._curPage, 1));
+            runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._freeform_scale = 1));
+            this.gotoPage(NumCast(this.props.Document._layout_curPage, 1));
         }
         document.removeEventListener('pagesinit', this.pagesinit);
         var quickScroll: { loc?: string; easeFunc?: 'ease' | 'linear' } | undefined = { loc: this._initialScroll ? this._initialScroll.loc?.toString() : '', easeFunc: this._initialScroll ? this._initialScroll.easeFunc : undefined };
         this._disposers.scale = reaction(
-            () => NumCast(this.props.layoutDoc._viewScale, 1),
+            () => NumCast(this.props.layoutDoc._freeform_scale, 1),
             scale => (this._pdfViewer.currentScaleValue = scale),
             { fireImmediately: true }
         );
         this._disposers.scroll = reaction(
-            () => Math.abs(NumCast(this.props.Document._scrollTop)),
+            () => Math.abs(NumCast(this.props.Document._layout_scrollTop)),
             pos => {
                 if (!this._ignoreScroll) {
                     this._showWaiting && this.setupPdfJsViewer();
@@ -316,7 +316,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if (this._mainCont.current && !this._forcedScroll) {
             this._ignoreScroll = true; // the pdf scrolled, so we need to tell the Doc to scroll but we don't want the doc to then try to set the PDF scroll pos (which would interfere with the smooth scroll animation)
             if (!LinkDocPreview.LinkInfo) {
-                this.props.layoutDoc._scrollTop = this._mainCont.current.scrollTop;
+                this.props.layoutDoc._layout_scrollTop = this._mainCont.current.scrollTop;
             }
             this._ignoreScroll = false;
             if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio
@@ -371,7 +371,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         // if alt+left click, drag and annotate
         this._downX = e.clientX;
         this._downY = e.clientY;
-        if ((this.props.Document._viewScale || 1) !== 1) return;
+        if ((this.props.Document._freeform_scale || 1) !== 1) return;
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
@@ -421,7 +421,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
 
         // Changing which document to add the annotation to (the currently selected PDF)
-        GPTPopup.Instance.setSidebarId('data-sidebar');
+        GPTPopup.Instance.setSidebarId('data_sidebar');
         GPTPopup.Instance.addDoc = this.props.sidebarAddDoc;
     };
 
@@ -434,7 +434,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 const rect = clientRects.item(i);
                 if (rect?.width && rect.width < this._mainCont.current.clientWidth / this.props.ScreenToLocalTransform().Scale) {
                     const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
-                    const pdfScale = NumCast(this.props.layoutDoc._viewScale, 1);
+                    const pdfScale = NumCast(this.props.layoutDoc._freeform_scale, 1);
                     const annoBox = document.createElement('div');
                     annoBox.className = 'marqueeAnnotator-annotationBox';
                     // transforms the positions from screen onto the pdf div
@@ -477,7 +477,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             if (e.ctrlKey) {
                 const curScale = Number(this._pdfViewer.currentScaleValue);
                 this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - (curScale * e.deltaY) / 1000));
-                this.props.layoutDoc._viewScale = Number(this._pdfViewer.currentScaleValue);
+                this.props.layoutDoc._freeform_scale = Number(this._pdfViewer.currentScaleValue);
             }
         }
     };
@@ -485,12 +485,12 @@ export class PDFViewer extends React.Component<IViewerProps> {
     pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
     @computed get annotationLayer() {
         return (
-            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}>
+            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
                 {this.inlineTextAnnotations
                     .sort((a, b) => NumCast(a.y) - NumCast(b.y))
                     .filter(anno => !anno.hidden)
                     .map(anno => (
-                        <Annotation {...this.props} fieldKey={this.props.fieldKey + '-annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                        <Annotation {...this.props} fieldKey={this.props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
                     ))}
             </div>
         );
@@ -508,8 +508,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     getScrollHeight = () => this._scrollHeight;
     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
-    scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform());
-    overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
+    scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._layout_scrollTop)) : this.props.ScreenToLocalTransform());
+    overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._freeform_scale, 1));
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
@@ -539,7 +539,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 childPointerEvents="all" // but freeform children need to get events to allow text editing, etc
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
-                fieldKey={this.props.fieldKey + '-annotations'}
+                fieldKey={this.props.fieldKey + '_annotations'}
                 getScrollHeight={this.getScrollHeight}
                 setPreviewCursor={this.setPreviewCursor}
                 setBrushViewer={this.setBrushViewer}
@@ -548,7 +548,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 ScreenToLocalTransform={this.overlayTransform}
                 isAnyChildContentActive={returnFalse}
                 isAnnotationOverlayScrollable={true}
-                dropAction={'alias'}
+                dropAction="embed"
                 docFilters={docFilters}
                 select={emptyFunction}
                 bringToFront={emptyFunction}
@@ -586,8 +586,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     onPointerDown={this.onPointerDown}
                     onClick={this.onClick}
                     style={{
-                        overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? 'scroll' : undefined,
-                        height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `100%`,
+                        overflowX: NumCast(this.props.layoutDoc._freeform_scale, 1) !== 1 ? 'scroll' : undefined,
+                        height: !this.props.Document._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `100%`,
                     }}>
                     {this.pdfViewerDiv}
                     {this.annotationLayer}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 32b661bb6..536ec7c75 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -47,7 +47,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     @observable _linkedDocsIn: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
     @observable _selectedResult: Doc | undefined = undefined;
     @observable _deletedDocsStatus: boolean = false;
-    @observable _onlyAliases: boolean = true;
+    @observable _onlyEmbeddings: boolean = true;
 
     /**
      * This is the constructor for the SearchBox class.
@@ -139,7 +139,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 visited.push(d);
                 const fieldKey = Doc.LayoutFieldKey(d);
                 const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
-                const data = d[annos ? fieldKey + '-annotations' : fieldKey];
+                const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                 data && newarray.push(...DocListCast(data));
                 func(depth, d);
             });
@@ -166,7 +166,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                     .map(async d => {
                         const fieldKey = Doc.LayoutFieldKey(d);
                         const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
-                        const data = d[annos ? fieldKey + '-annotations' : fieldKey];
+                        const data = d[annos ? fieldKey + '_annotations' : fieldKey];
                         const docs = await DocListCastAsync(data);
                         docs && newarray.push(...docs);
                         func(depth, d);
@@ -217,34 +217,34 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             'y',
             'proto',
             'width',
-            'autoHeight',
+            'layout_autoHeight',
             'acl-Override',
             'acl-Public',
-            'context',
+            'embedContainer',
             'zIndex',
             'height',
-            'text-scrollHeight',
-            'text-height',
+            'text_scrollHeight',
+            'text_height',
             'cloneFieldFilter',
-            'isPrototype',
-            'text-annotations',
-            'dragFactory-count',
-            'text-noTemplate',
-            'aliases',
-            'system',
-            'layoutKey',
-            'baseProto',
+            'isDataDoc',
+            'text_annotations',
+            'dragFactory_count',
+            'text_noTemplate',
+            'proto_embeddings',
+            'isSystem',
+            'layout_fieldKey',
+            'isBaseProto',
             'xMargin',
             'yMargin',
             'links',
             'layout',
             'layout_keyValue',
-            'fitWidth',
+            'layout_fitWidth',
             'viewType',
-            'title-custom',
-            'panX',
-            'panY',
-            'viewScale',
+            'title_custom',
+            'freeform_panX',
+            'freeform_panY',
+            'freeform_scale',
         ];
         query = query.toLowerCase();
 
@@ -300,8 +300,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 const linkedDocSet = new Set<Doc>();
 
                 Doc.GetProto(doc)[DirectLinksSym].forEach(link => {
-                    const d1 = link?.anchor1 as Doc;
-                    const d2 = link?.anchor2 as Doc;
+                    const d1 = link?.link_anchor_1 as Doc;
+                    const d2 = link?.link_anchor_2 as Doc;
                     if (doc === d1 && this._results.has(d2)) {
                         linkedDocSet.add(d2);
                         this._linkedDocsIn.get(d2)?.add(doc);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5e3e27a7c..deda4c876 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -236,7 +236,7 @@ export class Doc extends RefField {
         if (
             doc &&
             Doc.MyFileOrphans instanceof Doc &&
-            Doc.IsPrototype(doc) &&
+            Doc.IsDocDataProto(doc) &&
             !Doc.IsSystem(doc) &&
             ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
             !doc.isFolder &&
@@ -381,7 +381,7 @@ export class Doc extends RefField {
         const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
         if (templateLayoutDoc) {
             let renderFieldKey: any;
-            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, 'layout')];
+            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
             if (typeof layoutField === 'string') {
                 renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0]; //layoutField.split("'")[1];
             } else {
@@ -510,14 +510,14 @@ export namespace Doc {
     export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
         return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
     }
-    export function IsPrototype(doc: Doc) {
-        return GetT(doc, 'isPrototype', 'boolean', true);
+    export function IsDocDataProto(doc: Doc) {
+        return GetT(doc, 'isDataDoc', 'boolean', true);
     }
     export function IsBaseProto(doc: Doc) {
-        return GetT(doc, 'baseProto', 'boolean', true);
+        return GetT(doc, 'isBaseProto', 'boolean', true);
     }
     export function IsSystem(doc: Doc) {
-        return GetT(doc, 'system', 'boolean', true);
+        return GetT(doc, 'isSystem', 'boolean', true);
     }
     export function IsDelegateField(doc: Doc, fieldKey: string) {
         return doc && Get(doc, fieldKey, true) !== undefined;
@@ -574,9 +574,9 @@ export namespace Doc {
 
     // Gets the data document for the document.  Note: this is mis-named -- it does not specifically
     // return the doc's proto, but rather recursively searches through the proto inheritance chain
-    // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype').
+    // and returns the document who's proto is undefined or whose proto is marked as a data doc ('isDataDoc').
     export function GetProto(doc: Doc): Doc {
-        const proto = doc && (Doc.GetT(doc, 'isPrototype', 'boolean', true) ? doc : doc.proto || doc);
+        const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : doc.proto || doc);
         return proto === doc ? proto : Doc.GetProto(proto);
     }
     export function GetDataDoc(doc: Doc): Doc {
@@ -680,25 +680,25 @@ export namespace Doc {
         return bounds;
     }
 
-    export function MakeAlias(doc: Doc, id?: string) {
-        const alias = !GetT(doc, 'isPrototype', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
-        const layout = Doc.LayoutField(alias);
-        if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) {
-            Doc.SetLayout(alias, Doc.MakeAlias(layout));
+    export function MakeEmbedding(doc: Doc, id?: string) {
+        const embedding = !GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
+        const layout = Doc.LayoutField(embedding);
+        if (layout instanceof Doc && layout !== embedding && layout === Doc.Layout(embedding)) {
+            Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
         }
-        alias.aliasOf = doc;
-        alias.aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
-        alias.title = ComputedField.MakeFunction(`renameAlias(this)`);
-        alias.author = Doc.CurrentUserEmail;
+        embedding.createdFrom = doc;
+        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = NumCast(Doc.GetProto(doc).proto_embeddingId) + 1;
+        embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
+        embedding.author = Doc.CurrentUserEmail;
 
-        Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'aliases', alias);
+        Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'proto_embeddings', embedding);
 
-        return alias;
+        return embedding;
     }
 
-    export function BestAlias(doc: Doc) {
-        const bestAlias = Doc.GetProto(doc) ? DocListCast(doc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : doc;
-        return bestAlias ?? Doc.MakeAlias(doc);
+    export function BestEmbedding(doc: Doc) {
+        const bestEmbedding = Doc.GetProto(doc) ? DocListCast(doc.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+        return bestEmbedding ?? Doc.MakeEmbedding(doc);
     }
 
     // this lists out all the tag ids that can be  in a RichTextField that might contain document ids.
@@ -745,7 +745,7 @@ export namespace Doc {
                 if (docAtKey instanceof Doc) {
                     if (pruneDocs.includes(docAtKey)) {
                         // prune doc and do nothing
-                    } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['context', 'annotationOn', 'proto'].includes(key) || ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) {
+                    } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['embedContainer', 'annotationOn', 'proto'].includes(key) || ((key === 'link_anchor_1' || key === 'link_anchor_2') && doc.author === Doc.CurrentUserEmail))) {
                         assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
                     } else {
                         assignKey(docAtKey);
@@ -766,7 +766,8 @@ export namespace Doc {
         Array.from(doc[DirectLinksSym]).forEach(async link => {
             if (
                 cloneLinks ||
-                ((cloneMap.has(DocCast(link.anchor1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.anchor1)?.annotationOn)?.[Id])) && (cloneMap.has(DocCast(link.anchor2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.anchor2)?.annotationOn)?.[Id])))
+                ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) &&
+                    (cloneMap.has(DocCast(link.link_anchor_2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id])))
             ) {
                 linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
             }
@@ -804,7 +805,7 @@ export namespace Doc {
     export async function MakeClone(doc: Doc, cloneLinks = true, cloneMap: Map<string, Doc> = new Map()) {
         const linkMap = new Map<string, Doc>();
         const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
-        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.context ? [DocCast(doc.context)] : [], cloneLinks);
+        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks);
         const repaired = new Set<Doc>();
         const linkedDocs = Array.from(linkMap.values());
         linkedDocs.map((link: Doc) => LinkManager.Instance.addLink(link, true));
@@ -980,7 +981,7 @@ export namespace Doc {
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
         const copy = new Doc(copyProtoId, true);
         updateCachedAcls(copy);
-        const exclude = Cast(doc.cloneFieldFilter, listSpec('string'), []);
+        const exclude = [...Cast(doc.cloneFieldFilter, listSpec('string'), []), 'dragFactory_count', 'cloneFieldFilter'];
         Object.keys(doc).forEach(key => {
             if (exclude.includes(key)) return;
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
@@ -1009,12 +1010,12 @@ export namespace Doc {
             }
         });
         if (copyProto) {
-            Doc.GetProto(copy).context = undefined;
-            Doc.GetProto(copy).aliases = new List<Doc>([copy]);
+            Doc.GetProto(copy).embedContainer = undefined;
+            Doc.GetProto(copy).proto_embeddings = new List<Doc>([copy]);
         } else {
-            Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'aliases', copy);
+            Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'proto_embeddings', copy);
         }
-        copy.context = undefined;
+        copy.embedContainer = undefined;
         Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
@@ -1035,7 +1036,7 @@ export namespace Doc {
             Object.keys(doc)
                 .filter(key => key.startsWith('acl'))
                 .forEach(key => (delegate[key] = doc[key]));
-            if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'aliases', delegate);
+            if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'proto_embeddings', delegate);
             title && (delegate.title = title);
             delegate[Initializing] = false;
             Doc.AddFileOrphan(delegate);
@@ -1053,13 +1054,13 @@ export namespace Doc {
         delegateProto[Initializing] = true;
         delegateProto.proto = doc;
         delegateProto.author = Doc.CurrentUserEmail;
-        delegateProto.isPrototype = true;
+        delegateProto.isDataDoc = true;
         title && (delegateProto.title = title);
         const delegate = new Doc(id, true);
         delegate[Initializing] = true;
         delegate.proto = delegateProto;
         delegate.author = Doc.CurrentUserEmail;
-        Doc.AddDocToList(delegateProto[DataSym], 'aliases', delegate);
+        Doc.AddDocToList(delegateProto[DataSym], 'proto_embeddings', delegate);
         delegate[Initializing] = false;
         delegateProto[Initializing] = false;
         return delegate;
@@ -1071,9 +1072,9 @@ export namespace Doc {
             const proto = new Doc();
             proto.author = Doc.CurrentUserEmail;
             const target = Doc.MakeDelegate(proto);
-            const targetKey = StrCast(templateDoc.layoutKey, 'layout');
+            const targetKey = StrCast(templateDoc.layout_fieldKey, 'layout');
             const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
-            target.layoutKey = targetKey;
+            target.layout_fieldKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
             Doc.defaultAclPrivate && (applied['acl-Public'] = 'Not Shared');
             return applied;
@@ -1176,31 +1177,31 @@ export namespace Doc {
         return overrideLayout || doc[LayoutSym] || doc;
     }
     export function SetLayout(doc: Doc, layout: Doc | string) {
-        doc[StrCast(doc.layoutKey, 'layout')] = layout;
+        doc[StrCast(doc.layout_fieldKey, 'layout')] = layout;
     }
     export function LayoutField(doc: Doc) {
-        return doc[StrCast(doc.layoutKey, 'layout')];
+        return doc[StrCast(doc.layout_fieldKey, 'layout')];
     }
     export function LayoutFieldKey(doc: Doc): string {
-        return StrCast(Doc.Layout(doc).layout).split("'")[1]; // bcz: TODO check on this .  used to always reference 'layout', now it uses the layout speicfied by the current layoutKey
+        return StrCast(Doc.Layout(doc).layout).split("'")[1]; // bcz: TODO check on this .  used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
     }
     export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
         return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
     }
     export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
-        return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeWidth'], useWidth ? doc[WidthSym]() : 0));
+        return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? doc[WidthSym]() : 0));
     }
     export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
         if (!doc) return 0;
         const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]()) / doc[WidthSym]();
-        const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeHeight'], useHeight ? doc[HeightSym]() : 0);
+        const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? doc[HeightSym]() : 0);
         return NumCast(doc._nativeHeight, nheight || dheight);
     }
     export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) {
-        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '-nativeWidth'] = width;
+        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeWidth'] = width;
     }
     export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) {
-        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '-nativeHeight'] = height;
+        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
     }
 
     const manager = new DocData();
@@ -1271,8 +1272,8 @@ export namespace Doc {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
             if (lastBrushed) {
                 for (const link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
-                    const a1 = Cast(link.anchor1, Doc, null);
-                    const a2 = Cast(link.anchor2, Doc, null);
+                    const a1 = Cast(link.link_anchor_1, Doc, null);
+                    const a2 = Cast(link.link_anchor_2, Doc, null);
                     if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) || Doc.AreProtosEqual(Cast(a1.annotationOn, Doc, null), doc) || Doc.AreProtosEqual(Cast(a2.annotationOn, Doc, null), doc)) {
                         return DocBrushStatus.linkHighlighted;
                     }
@@ -1304,8 +1305,8 @@ export namespace Doc {
     }
 
     export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) {
-        if (linkDoc.anchor2 === anchorDoc || (linkDoc.anchor2 as Doc).annotationOn) return '2';
-        return Doc.AreProtosEqual(anchorDoc, (linkDoc.anchor1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.anchor1 as Doc) ? '1' : '2';
+        if (linkDoc.link_anchor_2 === anchorDoc || (linkDoc.link_anchor_2 as Doc).annotationOn) return '2';
+        return Doc.AreProtosEqual(anchorDoc, (linkDoc.link_anchor_1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.link_anchor_1 as Doc) ? '1' : '2';
     }
 
     export function linkFollowUnhighlight() {
@@ -1400,14 +1401,14 @@ export namespace Doc {
     }
 
     export function deiconifyView(doc: Doc) {
-        StrCast(doc.layoutKey).split('_')[1] === 'icon' && setNativeView(doc);
+        StrCast(doc.layout_fieldKey).split('_')[1] === 'icon' && setNativeView(doc);
     }
 
     export function setNativeView(doc: any) {
-        const prevLayout = StrCast(doc.layoutKey).split('_')[1];
+        const prevLayout = StrCast(doc.layout_fieldKey).split('_')[1];
         const deiconify = prevLayout === 'icon' && StrCast(doc.deiconifyLayout) ? 'layout_' + StrCast(doc.deiconifyLayout) : '';
         prevLayout === 'icon' && (doc.deiconifyLayout = undefined);
-        doc.layoutKey = deiconify || 'layout';
+        doc.layout_fieldKey = deiconify || 'layout';
     }
     export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: number[]) {
         if (!container) return;
@@ -1431,7 +1432,7 @@ export namespace Doc {
     // based on the modifiers :"check", "x", undefined
     export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
         if (!container) return;
-        const filterField = '_' + (fieldPrefix ? fieldPrefix + '-' : '') + 'docFilters';
+        const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'docFilters';
         const docFilters = Cast(container[filterField], listSpec('string'), []);
         runInAction(() => {
             for (let i = 0; i < docFilters.length; i++) {
@@ -1471,18 +1472,18 @@ export namespace Doc {
     export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) {
         runInAction(() => {
             if (Doc.NativeWidth(layoutDoc) || Doc.NativeHeight(layoutDoc)) {
-                layoutDoc._viewScale = NumCast(layoutDoc._viewScale, 1) * contentScale;
+                layoutDoc._freeform_scale = NumCast(layoutDoc._freeform_scale, 1) * contentScale;
                 layoutDoc._nativeWidth = undefined;
                 layoutDoc._nativeHeight = undefined;
-                layoutDoc._forceReflow = undefined;
+                layoutDoc._layout_forceReflow = undefined;
                 layoutDoc._nativeHeightUnfrozen = undefined;
                 layoutDoc._nativeDimModifiable = undefined;
             } else {
-                layoutDoc._autoHeight = false;
+                layoutDoc._layout_autoHeight = false;
                 if (!Doc.NativeWidth(layoutDoc)) {
                     layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
                     layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
-                    layoutDoc._forceReflow = true;
+                    layoutDoc._layout_forceReflow = true;
                     layoutDoc._nativeHeightUnfrozen = true;
                     layoutDoc._nativeDimModifiable = true;
                 }
@@ -1710,8 +1711,8 @@ export function IdToDoc(id: string) {
 ScriptingGlobals.add(function idToDoc(id: string): any {
     return IdToDoc(id);
 });
-ScriptingGlobals.add(function renameAlias(doc: any) {
-    return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.aliasNumber})`;
+ScriptingGlobals.add(function renameEmbedding(doc: any) {
+    return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
 });
 ScriptingGlobals.add(function getProto(doc: any) {
     return Doc.GetProto(doc);
@@ -1719,8 +1720,8 @@ ScriptingGlobals.add(function getProto(doc: any) {
 ScriptingGlobals.add(function getDocTemplate(doc?: any) {
     return Doc.getDocTemplate(doc);
 });
-ScriptingGlobals.add(function getAlias(doc: any) {
-    return Doc.MakeAlias(doc);
+ScriptingGlobals.add(function getEmbedding(doc: any) {
+    return Doc.MakeEmbedding(doc);
 });
 ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
     return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto);
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 239b59e83..24cd078f2 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -395,11 +395,11 @@ export namespace RichTextUtils {
                             if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
                                 const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]);
                                 if (linkDoc instanceof Doc) {
-                                    let exported = (await Cast(linkDoc.anchor2, Doc))!;
+                                    let exported = (await Cast(linkDoc.link_anchor_2, Doc))!;
                                     if (!exported.customLayout) {
-                                        exported = Doc.MakeAlias(exported);
+                                        exported = Doc.MakeEmbedding(exported);
                                         DocUtils.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
-                                        linkDoc.anchor2 = exported;
+                                        linkDoc.link_anchor_2 = exported;
                                     }
                                     url = Utils.shareUrl(exported[Id]);
                                 }
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 4cf286a32..251b1149d 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -3,7 +3,7 @@ import { List } from './List';
 import { RefField } from './RefField';
 import { DateField } from './DateField';
 import { ScriptField } from './ScriptField';
-import { URLField, WebField, ImageField } from './URLField';
+import { URLField, WebField, ImageField, CsvField } from './URLField';
 import { TextField } from '@material-ui/core';
 import { RichTextField } from './RichTextField';
 
@@ -104,6 +104,9 @@ export function RTFCast(field: FieldResult) {
 export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
     return Cast(field, ScriptField, defaultVal);
 }
+export function CsvCast(field: FieldResult, defaultVal: CsvField | null = null) {
+    return Cast(field, CsvField, defaultVal);
+}
 export function WebCast(field: FieldResult, defaultVal: WebField | null = null) {
     return Cast(field, WebField, defaultVal);
 }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index b7fd06973..7b2fd74d0 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -13,11 +13,11 @@ export const documentSchema = createSchema({
     links: listSpec(Doc), // computed (readonly) list of links associated with this document
 
     // "Location" properties in a very general sense
-    _curPage: 'number', // current page of a page based document
+    _layout_curPage: 'number', // current page of a page based document
     _currentFrame: 'number', // current frame of a frame based collection (e.g., a progressive slide)
     lastFrame: 'number', // last frame of a frame based collection (e.g., a progressive slide)
     activeFrame: 'number', // the active frame of a frame based animated document
-    _currentTimecode: 'number', // current play back time of a temporal document (video / audio)
+    _layout_currentTimecode: 'number', // current play back time of a temporal document (video / audio)
     _timecodeToShow: 'number', // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
     _timecodeToHIde: 'number', // the time that a document should be hidden
     markers: listSpec(Doc), // list of markers for audio / video
@@ -25,52 +25,52 @@ export const documentSchema = createSchema({
     y: 'number', // y coordinate when in a freeform view
     z: 'number', // z "coordinate" - non-zero specifies the overlay layer of a freeformview
     zIndex: 'number', // zIndex of a document in a freeform view
-    _scrollTop: 'number', // scroll position of a scrollable document (pdf, text, web)
+    _layout_scrollTop: 'number', // scroll position of a scrollable document (pdf, text, web)
     lat: 'number',
     lng: 'number',
 
     // appearance properties on the layout
     '_backgroundGrid-spacing': 'number', // the size of the grid for collection views
-    _autoHeight: 'boolean', // whether the height of the document should be computed automatically based on its contents
+    _layout_autoHeight: 'boolean', // whether the height of the document should be computed automatically based on its contents
     _nativeWidth: 'number', // native width of document which determines how much document contents are scaled when the document's width is set
     _nativeHeight: 'number', // "
     _width: 'number', // width of document in its container's coordinate system
     _height: 'number', // "
-    _xPadding: 'number', // pixels of padding on left/right of collectionfreeformview contents when fitContentsToBox is set
-    _yPadding: 'number', // pixels of padding on top/bottom of collectionfreeformview contents when fitContentsToBox is set
+    _xPadding: 'number', // pixels of padding on left/right of collectionfreeformview contents when freeform_fitContentsToBox is set
+    _yPadding: 'number', // pixels of padding on top/bottom of collectionfreeformview contents when freeform_fitContentsToBox is set
     _xMargin: 'number', // margin added on left/right of most documents to add separation from their container
     _yMargin: 'number', // margin added on top/bottom of most documents to add separation from their container
     _overflow: 'string', // sets overflow behvavior for CollectionFreeForm views
-    _showCaption: 'string', // whether editable caption text is overlayed at the bottom of the document
-    _showTitle: 'string', // the fieldkey(s) whose contents should be displayed at the top of the document. separate multiple keys with ";".  Use :hover suffix to indicate title should be shown on hover
+    _layout_showCaption: 'string', // whether editable caption text is overlayed at the bottom of the document
+    _layout_showTitle: 'string', // the fieldkey(s) whose contents should be displayed at the top of the document. separate multiple keys with ";".  Use :hover suffix to indicate title should be shown on hover
     _pivotField: 'string', // specifies which field key should be used as the timeline/pivot axis
     _columnsFill: 'boolean', // whether documents in a stacking view column should be sized to fill the column
     _columnsSort: 'string', // how a document should be sorted "ascending", "descending", undefined (none)
     _columnsHideIfEmpty: 'boolean', // whether empty stacking view column headings should be hidden
-    _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
-    _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
+    // _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
+    // _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
     _fontSize: 'string',
     _fontFamily: 'string',
-    _sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
+    _layout_sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
 
     // appearance properties on the data document
     backgroundColor: 'string', // background color of document
     borderRounding: 'string', // border radius rounding of document
     boxShadow: 'string', // the amount of shadow around the perimeter of a document
     color: 'string', // foreground color of document
-    fitContentsToBox: 'boolean', // whether freeform view contents should be zoomed/panned to fill the area of the document view box
+    freeform_fitContentsToBox: 'boolean', // whether freeform view contents should be zoomed/panned to fill the area of the document view box
     fontSize: 'string',
     hidden: 'boolean', // whether a document should not be displayed
-    isInkMask: 'boolean', // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
-    layout: 'string', // this is the native layout string for the document.  templates can be added using other fields and setting layoutKey below
-    layoutKey: 'string', // holds the field key for the field that actually holds the current lyoat
+    stroke_isInkMask: 'boolean', // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
+    layout: 'string', // this is the native layout string for the document.  templates can be added using other fields and setting layout_fieldKey below
+    layout_fieldKey: 'string', // holds the field key for the field that actually holds the current lyoat
     letterSpacing: 'string',
     opacity: 'number', // opacity of document
-    strokeWidth: 'number',
-    strokeBezier: 'number',
-    strokeStartMarker: 'string',
-    strokeEndMarker: 'string',
-    strokeDash: 'string',
+    stroke_width: 'number',
+    stroke_bezier: 'number',
+    stroke_startMarker: 'string',
+    stroke_endMarker: 'string',
+    stroke_dash: 'string',
     textTransform: 'string',
     treeViewOpen: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
     treeViewExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
@@ -87,20 +87,20 @@ export const documentSchema = createSchema({
     followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, )
     hideLinkButton: 'boolean', // whether the blue link counter button should be hidden
     hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
-    linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
+    layout_linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
     isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations
     layers: listSpec('string'), // which layers the document is part of
     _lockedPosition: 'boolean', // whether the document can be moved (dragged)
     _lockedTransform: 'boolean', // whether a freeformview can pan/zoom
-    linkDisplayArrow: 'boolean', // toggles directed arrows
+    layout_linkDisplayArrow: 'boolean', // toggles directed arrows
 
     // drag drop properties
     _stayInCollection: 'boolean', // whether document can be dropped into a different collection
     dragFactory: Doc, // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    dropAction: 'string', // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
-    targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
-    childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy")
-    removeDropProperties: listSpec('string'), // properties that should be removed from the alias/copy/etc of this document when it is dropped
+    dropAction: 'string', // override specifying what should happen when this document is dropped (can be "embed", "copy", "move")
+    targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("embed", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
+    childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
+    removeDropProperties: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
 });
 
 export const collectionSchema = createSchema({
diff --git a/src/fields/util.ts b/src/fields/util.ts
index d5b55867e..2a6caaaa3 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -289,7 +289,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         });
 
         // maps over the annotations of the document
-        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).forEach(d => {
+        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
             distributeAcls(key, acl, d, inheritingFromCollection, visited);
             distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited);
         });
@@ -377,7 +377,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
             diff?.op === '$addToSet'
                 ? { $addToSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } }
                 : diff?.op === '$remFromSet'
-                ? { $remFromSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)), hint : diff.hint } }
+                ? { $remFromSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)), hint: diff.hint } }
                 : { $set: { ['fields.' + prop]: SerializationHelper.Serialize(value) } };
         !op.$set && ((op as any).length = diff.length);
         const prevValue = ObjectField.MakeCopy(lastValue as List<any>);
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index a0b437354..c3423e2a5 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -21,7 +21,13 @@ import React = require('react');
 export class AudioUpload extends React.Component {
     @observable public _audioCol: Doc = FieldValue(
         Cast(
-            Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: 'mobile audio', _width: 500, _height: 100 }), Doc) as Doc], { title: 'mobile audio', _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: '0 0' }),
+            Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: 'mobile audio', _width: 500, _height: 100 }), Doc) as Doc], {
+                title: 'mobile audio',
+                _width: 300,
+                _height: 300,
+                _layoutFitContentsToBox: true,
+                boxShadow: '0 0',
+            }),
             Doc
         )
     ) as Doc;
@@ -48,7 +54,7 @@ export class AudioUpload extends React.Component {
                             Doc
                         ) as Doc,
                     ],
-                    { title: 'mobile audio', _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: '0 0' }
+                    { title: 'mobile audio', _width: 300, _height: 300, _layoutFitContentsToBox: true, boxShadow: '0 0' }
                 ),
                 Doc
             )
diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts
index a74e13a62..186f0bcd3 100644
--- a/src/server/ApiManagers/SearchManager.ts
+++ b/src/server/ApiManagers/SearchManager.ts
@@ -1,94 +1,89 @@
-import { exec } from "child_process";
-import { cyan, green, red, yellow } from "colors";
+import { exec } from 'child_process';
+import { cyan, green, red, yellow } from 'colors';
 import * as path from 'path';
-import { log_execution } from "../ActionUtilities";
-import { Database } from "../database";
-import { Method } from "../RouteManager";
-import RouteSubscriber from "../RouteSubscriber";
-import { Search } from "../Search";
-import ApiManager, { Registration } from "./ApiManager";
-import { Directory, pathToDirectory } from "./UploadManager";
+import { log_execution } from '../ActionUtilities';
+import { Database } from '../database';
+import { Method } from '../RouteManager';
+import RouteSubscriber from '../RouteSubscriber';
+import { Search } from '../Search';
+import ApiManager, { Registration } from './ApiManager';
+import { Directory, pathToDirectory } from './UploadManager';
 const findInFiles = require('find-in-files');
 
 export class SearchManager extends ApiManager {
-
     protected initialize(register: Registration): void {
-
         register({
             method: Method.GET,
-            subscription: new RouteSubscriber("solr").add("action"),
+            subscription: new RouteSubscriber('solr').add('action'),
             secureHandler: async ({ req, res }) => {
                 const { action } = req.params;
                 switch (action) {
-                    case "start":
-                    case "stop":
-                        const status = req.params.action === "start";
+                    case 'start':
+                    case 'stop':
+                        const status = req.params.action === 'start';
                         SolrManager.SetRunning(status);
                         break;
-                    case "update":
+                    case 'update':
                         await SolrManager.update();
                         break;
                     default:
                         console.log(yellow(`${action} is an unknown solr operation.`));
                 }
-                res.redirect("/home");
-            }
+                res.redirect('/home');
+            },
         });
 
         register({
             method: Method.GET,
-            subscription: "/textsearch",
+            subscription: '/textsearch',
             secureHandler: async ({ req, res }) => {
                 const q = req.query.q;
                 if (q === undefined) {
                     res.send([]);
                     return;
                 }
-                const resObj: { ids: string[], numFound: number, lines: string[] } = { ids: [], numFound: 0, lines: [] };
+                const resObj: { ids: string[]; numFound: number; lines: string[] } = { ids: [], numFound: 0, lines: [] };
                 let results: any;
                 const dir = pathToDirectory(Directory.text);
                 try {
                     const regex = new RegExp(q.toString());
-                    results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, dir, ".txt$");
+                    results = await findInFiles.find({ term: q, flags: 'ig' }, dir, '.txt$');
                     for (const result in results) {
-                        resObj.ids.push(path.basename(result, ".txt").replace(/upload_/, ""));
+                        resObj.ids.push(path.basename(result, '.txt').replace(/upload_/, ''));
                         resObj.lines.push(results[result].line);
                         resObj.numFound++;
                     }
                     res.send(resObj);
                 } catch (e) {
-                    console.log(red("textsearch:bad RegExp" + q.toString()));
+                    console.log(red('textsearch:bad RegExp' + q.toString()));
                     res.send([]);
                     return;
                 }
-            }
+            },
         });
 
         register({
             method: Method.GET,
-            subscription: "/dashsearch",
+            subscription: '/dashsearch',
             secureHandler: async ({ req, res }) => {
                 const solrQuery: any = {};
-                ["q", "fq", "start", "rows", "sort", "hl.maxAnalyzedChars", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]);
+                ['q', 'fq', 'start', 'rows', 'sort', 'hl.maxAnalyzedChars', 'hl', 'hl.fl'].forEach(key => (solrQuery[key] = req.query[key]));
                 if (solrQuery.q === undefined) {
                     res.send([]);
                     return;
                 }
                 const results = await Search.search(solrQuery);
                 res.send(results);
-            }
+            },
         });
-
     }
-
 }
 
 export namespace SolrManager {
-
     export function SetRunning(status: boolean) {
-        const args = status ? "start" : "stop -p 8983";
+        const args = status ? 'start' : 'stop -p 8983';
         console.log(`solr management: trying to ${args}`);
-        exec(`solr ${args}`, { cwd: "./solr-8.3.1/bin" }, (error, stdout, stderr) => {
+        exec(`solr ${args}`, { cwd: './solr-8.3.1/bin' }, (error, stdout, stderr) => {
             if (error) {
                 console.log(red(`solr management error: unable to ${args} server`));
                 console.log(red(error.message));
@@ -97,39 +92,39 @@ export namespace SolrManager {
             console.log(yellow(stderr));
         });
         if (status) {
-            console.log(cyan("Start script is executing: please allow 15 seconds for solr to start on port 8983."));
+            console.log(cyan('Start script is executing: please allow 15 seconds for solr to start on port 8983.'));
         }
     }
 
     export async function update() {
-        console.log(green("Beginning update..."));
+        console.log(green('Beginning update...'));
         await log_execution<void>({
-            startMessage: "Clearing existing Solr information...",
-            endMessage: "Solr information successfully cleared",
+            startMessage: 'Clearing existing Solr information...',
+            endMessage: 'Solr information successfully cleared',
             action: Search.clear,
-            color: cyan
+            color: cyan,
         });
         const cursor = await log_execution({
-            startMessage: "Connecting to and querying for all documents from database...",
+            startMessage: 'Connecting to and querying for all documents from database...',
             endMessage: ({ result, error }) => {
                 const success = error === null && result !== undefined;
                 if (!success) {
-                    console.log(red("Unable to connect to the database."));
+                    console.log(red('Unable to connect to the database.'));
                     process.exit(0);
                 }
-                return "Connection successful and query complete";
+                return 'Connection successful and query complete';
             },
             action: () => Database.Instance.query({}),
-            color: yellow
+            color: yellow,
         });
         const updates: any[] = [];
         let numDocs = 0;
         function updateDoc(doc: any) {
             numDocs++;
-            if ((numDocs % 50) === 0) {
+            if (numDocs % 50 === 0) {
                 console.log(`Batch of 50 complete, total of ${numDocs}`);
             }
-            if (doc.__type !== "Doc") {
+            if (doc.__type !== 'Doc') {
                 return;
             }
             const fields = doc.fields;
@@ -143,8 +138,8 @@ export namespace SolrManager {
                 const term = ToSearchTerm(value);
                 if (term !== undefined) {
                     const { suffix, value } = term;
-                    if (key.endsWith('lastModified')) {
-                        update["lastModified" + suffix] = value;
+                    if (key.endsWith('modificationDate')) {
+                        update['modificationDate' + suffix] = value;
                     }
                     update[key + suffix] = value;
                     dynfield = true;
@@ -157,51 +152,54 @@ export namespace SolrManager {
         await cursor?.forEach(updateDoc);
         const result = await log_execution({
             startMessage: `Dispatching updates for ${updates.length} documents`,
-            endMessage: "Dispatched updates complete",
+            endMessage: 'Dispatched updates complete',
             action: () => Search.updateDocuments(updates),
-            color: cyan
+            color: cyan,
         });
         try {
             if (result) {
                 const { status } = JSON.parse(result).responseHeader;
-                console.log(status ? red(`Failed with status code (${status})`) : green("Success!"));
+                console.log(status ? red(`Failed with status code (${status})`) : green('Success!'));
             } else {
-                console.log(red("Solr is likely not running!"));
+                console.log(red('Solr is likely not running!'));
             }
         } catch (e) {
-            console.log(red("Error:"));
+            console.log(red('Error:'));
             console.log(e);
-            console.log("\n");
+            console.log('\n');
         }
         await cursor?.close();
     }
 
-    const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = {
-        "number": "_n",
-        "string": "_t",
-        "boolean": "_b",
-        "image": ["_t", "url"],
-        "video": ["_t", "url"],
-        "pdf": ["_t", "url"],
-        "audio": ["_t", "url"],
-        "web": ["_t", "url"],
-        "map": ["_t", "url"],
-        "date": ["_d", value => new Date(value.date).toISOString()],
-        "proxy": ["_i", "fieldId"],
-        "prefetch_proxy": ["_i", "fieldId"],
-        "list": ["_l", list => {
-            const results = [];
-            for (const value of list.fields) {
-                const term = ToSearchTerm(value);
-                if (term) {
-                    results.push(term.value);
+    const suffixMap: { [type: string]: string | [string, string | ((json: any) => any)] } = {
+        number: '_n',
+        string: '_t',
+        boolean: '_b',
+        image: ['_t', 'url'],
+        video: ['_t', 'url'],
+        pdf: ['_t', 'url'],
+        audio: ['_t', 'url'],
+        web: ['_t', 'url'],
+        map: ['_t', 'url'],
+        date: ['_d', value => new Date(value.date).toISOString()],
+        proxy: ['_i', 'fieldId'],
+        prefetch_proxy: ['_i', 'fieldId'],
+        list: [
+            '_l',
+            list => {
+                const results = [];
+                for (const value of list.fields) {
+                    const term = ToSearchTerm(value);
+                    if (term) {
+                        results.push(term.value);
+                    }
                 }
-            }
-            return results.length ? results : null;
-        }]
+                return results.length ? results : null;
+            },
+        ],
     };
 
-    function ToSearchTerm(val: any): { suffix: string, value: any } | undefined {
+    function ToSearchTerm(val: any): { suffix: string; value: any } | undefined {
         if (val === null || val === undefined) {
             return;
         }
@@ -213,7 +211,7 @@ export namespace SolrManager {
 
         if (Array.isArray(suffix)) {
             const accessor = suffix[1];
-            if (typeof accessor === "function") {
+            if (typeof accessor === 'function') {
                 val = accessor(val);
             } else {
                 val = val[accessor];
@@ -223,5 +221,4 @@ export namespace SolrManager {
 
         return { suffix, value: val };
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/server/updateProtos.ts b/src/server/updateProtos.ts
index c5552f6bf..2f3772a77 100644
--- a/src/server/updateProtos.ts
+++ b/src/server/updateProtos.ts
@@ -1,14 +1,22 @@
-import { Database } from "./database";
+import { Database } from './database';
 
-const protos =
-    ["text", "image", "web", "collection", "kvp", "video", "audio", "pdf", "icon", "import", "linkdoc", "map"];
+const protos = ['text', 'image', 'web', 'collection', 'kvp', 'video', 'audio', 'pdf', 'icon', 'import', 'linkdoc', 'map'];
 
 (async function () {
     await Promise.all(
-        protos.map(protoId => new Promise(res => Database.Instance.update(protoId, {
-            $set: { "fields.baseProto": true }
-        }, res)))
+        protos.map(
+            protoId =>
+                new Promise(res =>
+                    Database.Instance.update(
+                        protoId,
+                        {
+                            $set: { 'fields.isBaseProto': true },
+                        },
+                        res
+                    )
+                )
+        )
     );
 
-    console.log("done");
-})();
\ No newline at end of file
+    console.log('done');
+})();
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index f131927f7..be5cdb202 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -476,8 +476,8 @@ export namespace WebSocket {
                 if (term !== undefined) {
                     const { suffix, value } = term;
                     update[key + suffix] = { set: value };
-                    if (key.endsWith('lastModified')) {
-                        update['lastModified' + suffix] = value;
+                    if (key.endsWith('modificationDate')) {
+                        update['modificationDate' + suffix] = value;
                     }
                 }
             }
-- 
cgit v1.2.3-70-g09d2


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

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

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

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

