From abab63f89344e5bbbf0731e81e3ab7ddb0942664 Mon Sep 17 00:00:00 2001
From: ljungster <parkerljung@gmail.com>
Date: Tue, 15 Feb 2022 16:35:47 -0500
Subject: added commits

---
 src/client/views/nodes/trails/PresElementBox.tsx | 2 ++
 1 file changed, 2 insertions(+)

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

diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 238d025dc..b30e8cc13 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -169,6 +169,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
     /**
      * Function to drag and drop the pres element to a diferent location
      */
+    //TODO: this is what you need to look into
     startDrag = (e: PointerEvent) => {
         const miniView: boolean = this.toolbarWidth <= 100;
         const activeItem = this.rootDoc;
@@ -291,6 +292,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
         return width;
     }
 
+    // TODO: this is what you were looking for
     @computed get mainItem() {
         const isSelected: boolean = PresBox.Instance._selectedArray.has(this.rootDoc);
         const toolbarWidth: number = this.toolbarWidth;
-- 
cgit v1.2.3-70-g09d2


From 1a9f46e50551b618c7555e8e15e6d8e1394b0fe1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 21 Jul 2022 13:36:27 -0400
Subject: fixed dragging preselements by updating dragManger pointerup

---
 src/client/util/DragManager.ts                   | 323 ++++++++++++-----------
 src/client/views/nodes/trails/PresElementBox.tsx |   6 +-
 2 files changed, 166 insertions(+), 163 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 09b463c2f..4338072df 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,41 +1,35 @@
-import { action } from "mobx";
-import { DateField } from "../../fields/DateField";
-import { Doc, Field, Opt } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { PrefetchProxy } from "../../fields/Proxy";
-import { listSpec } from "../../fields/Schema";
-import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
-import { ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
-import { emptyFunction, Utils } from "../../Utils";
-import { Docs, DocUtils } from "../documents/Documents";
-import * as globalCssVariables from "../views/global/globalCssVariables.scss";
-import { DocumentView } from "../views/nodes/DocumentView";
-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
+import { action } from 'mobx';
+import { DateField } from '../../fields/DateField';
+import { Doc, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { PrefetchProxy } from '../../fields/Proxy';
+import { listSpec } from '../../fields/Schema';
+import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
+import { ScriptField } from '../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
+import { emptyFunction, Utils } from '../../Utils';
+import { Docs, DocUtils } from '../documents/Documents';
+import * as globalCssVariables from '../views/global/globalCssVariables.scss';
+import { DocumentView } from '../views/nodes/DocumentView';
+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
 
 /**
  * Initialize drag
- * @param _reference: The HTMLElement that is being dragged 
+ * @param _reference: The HTMLElement that is being dragged
  * @param docFunc: The Dash document being moved
  * @param moveFunc: The function called when the document is moved
  * @param dropAction: What to do with the document when it is dropped
  * @param dragStarted: Method to call when the drag is started
  */
-export function SetupDrag(
-    _reference: React.RefObject<HTMLElement>,
-    docFunc: () => Doc | Promise<Doc> | undefined,
-    moveFunc?: DragManager.MoveFunction,
-    dropAction?: dropActionType,
-    dragStarted?: () => void
-) {
+export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc> | undefined, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, dragStarted?: () => void) {
     const onRowMove = async (e: PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
 
-        document.removeEventListener("pointermove", onRowMove);
+        document.removeEventListener('pointermove', onRowMove);
         document.removeEventListener('pointerup', onRowUp);
         const doc = await docFunc();
         if (doc) {
@@ -47,7 +41,7 @@ export function SetupDrag(
         }
     };
     const onRowUp = (): void => {
-        document.removeEventListener("pointermove", onRowMove);
+        document.removeEventListener('pointermove', onRowMove);
         document.removeEventListener('pointerup', onRowUp);
     };
     const onItemDown = async (e: React.PointerEvent) => {
@@ -58,8 +52,8 @@ export function SetupDrag(
                 const dragDoc = await docFunc();
                 dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]);
             } else {
-                document.addEventListener("pointermove", onRowMove);
-                document.addEventListener("pointerup", onRowUp);
+                document.addEventListener('pointermove', onRowMove);
+                document.addEventListener('pointerup', onRowUp);
             }
         }
     };
@@ -69,15 +63,19 @@ export function SetupDrag(
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
     let dragLabel: HTMLDivElement;
-    export let StartWindowDrag: Opt<((e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void)>;
+    export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void>;
     export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
 
-    export function GetRaiseWhenDragged() { return BoolCast(Doc.UserDoc()._raiseWhenDragged); }
-    export function SetRaiseWhenDragged(val:boolean) { Doc.UserDoc()._raiseWhenDragged = val }
+    export function GetRaiseWhenDragged() {
+        return BoolCast(Doc.UserDoc()._raiseWhenDragged);
+    }
+    export function SetRaiseWhenDragged(val: boolean) {
+        Doc.UserDoc()._raiseWhenDragged = val;
+    }
     export function Root() {
-        const root = document.getElementById("root");
+        const root = document.getElementById('root');
         if (!root) {
-            throw new Error("No root element found");
+            throw new Error('No root element found');
         }
         return root;
     }
@@ -85,27 +83,20 @@ export namespace DragManager {
     export type MoveFunction = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
     export type RemoveFunction = (document: Doc | Doc[]) => boolean;
 
-    export interface DragDropDisposer { (): void; }
+    export interface DragDropDisposer {
+        (): void;
+    }
     export interface DragOptions {
         dragComplete?: (e: DragCompleteEvent) => void; // function to invoke when drag has completed
-        hideSource?: boolean;  // hide source document during drag
-        offsetX?: number;      // offset of top left of source drag visual from cursor
+        hideSource?: boolean; // hide source document during drag
+        offsetX?: number; // offset of top left of source drag visual from cursor
         offsetY?: number;
         noAutoscroll?: boolean;
     }
 
     // event called when the drag operation results in a drop action
     export class DropEvent {
-        constructor(
-            readonly x: number,
-            readonly y: number,
-            readonly complete: DragCompleteEvent,
-            readonly shiftKey: boolean,
-            readonly altKey: boolean,
-            readonly metaKey: boolean,
-            readonly ctrlKey: boolean,
-            readonly embedKey: boolean,
-        ) { }
+        constructor(readonly x: number, readonly y: number, readonly complete: DragCompleteEvent, readonly shiftKey: boolean, readonly altKey: boolean, readonly metaKey: boolean, readonly ctrlKey: boolean, readonly embedKey: boolean) {}
     }
 
     // event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
@@ -137,20 +128,22 @@ export namespace DragManager {
         treeViewDoc?: Doc;
         offset: number[];
         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'
+        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'
         removeDropProperties?: string[];
         moveDocument?: MoveFunction;
         removeDocument?: RemoveFunction;
         isDocDecorationMove?: boolean; // Flags that Document decorations are used to drag document which allows suppression of onDragStart scripts
     }
     export class LinkDragData {
-        constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc,) {
+        constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc) {
             this.linkDragView = dragView;
             this.linkSourceGetAnchor = linkSourceGetAnchor;
         }
-        get dragDocument() { return this.linkDragView.props.Document; }
+        get dragDocument() {
+            return this.linkDragView.props.Document;
+        }
         linkSourceGetAnchor: () => Doc;
         linkSourceDoc?: Doc;
         linkDragView: DocumentView;
@@ -177,43 +170,29 @@ export namespace DragManager {
         userDropAction: dropActionType;
     }
 
-    export function MakeDropTarget(
-        element: HTMLElement,
-        dropFunc: (e: Event, de: DropEvent) => void,
-        doc?: Doc,
-        preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void,
-    ): DragDropDisposer {
-        if ("canDrop" in element.dataset) {
-            throw new Error(
-                "Element is already droppable, can't make it droppable again"
-            );
+    export function MakeDropTarget(element: HTMLElement, dropFunc: (e: Event, de: DropEvent) => void, doc?: Doc, preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void): DragDropDisposer {
+        if ('canDrop' in element.dataset) {
+            throw new Error("Element is already droppable, can't make it droppable again");
         }
-        element.dataset.canDrop = "true";
+        element.dataset.canDrop = 'true';
         const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
         const preDropHandler = (e: Event) => {
             const de = (e as CustomEvent<DropEvent>).detail;
             preDropFunc?.(e, de, StrCast(doc?.targetDropAction) as dropActionType);
         };
-        element.addEventListener("dashOnDrop", handler);
-        doc && element.addEventListener("dashPreDrop", preDropHandler);
+        element.addEventListener('dashOnDrop', handler);
+        doc && element.addEventListener('dashPreDrop', preDropHandler);
         return () => {
-            element.removeEventListener("dashOnDrop", handler);
-            doc && element.removeEventListener("dashPreDrop", preDropHandler);
+            element.removeEventListener('dashOnDrop', handler);
+            doc && element.removeEventListener('dashPreDrop', preDropHandler);
             delete element.dataset.canDrop;
         };
     }
 
     // drag a document and drop it (or make an alias/copy on drop)
-    export function StartDocumentDrag(
-        eles: HTMLElement[],
-        dragData: DocumentDragData,
-        downX: number,
-        downY: number,
-        options?: DragOptions,
-        dropEvent?: () => any
-    ) {
+    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);
+            dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField());
             dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
             return dropDoc;
         };
@@ -222,19 +201,27 @@ export namespace DragManager {
             dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
-                docDragData.droppedDocuments =
-                    await Promise.all(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.MakeAlias(d) :
-                                docDragData.dropAction === "proto" ? Doc.GetProto(d) :
-                                    docDragData.dropAction === "copy" ? 
-                                        (await Doc.MakeClone(d)).clone : d));
-                !["same", "proto"].includes(docDragData.dropAction as any) && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
-                    const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
-                    const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
-                    remProps.map(prop => drop[prop] = undefined);
-                });
+                docDragData.droppedDocuments = await Promise.all(
+                    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.MakeAlias(d)
+                            : docDragData.dropAction === 'proto'
+                            ? Doc.GetProto(d)
+                            : docDragData.dropAction === 'copy'
+                            ? (
+                                  await Doc.MakeClone(d)
+                              ).clone
+                            : d
+                    )
+                );
+                !['same', 'proto'].includes(docDragData.dropAction as any) &&
+                    docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
+                        const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec('string'), []);
+                        const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
+                        remProps.map(prop => (drop[prop] = undefined));
+                    });
             }
             return e;
         };
@@ -243,23 +230,22 @@ export namespace DragManager {
         return true;
     }
 
-    // drag a button template and drop a new button 
-    export function
-        StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
+    // drag a button template and drop a new button
+    export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
         const finishDrag = (e: DragCompleteEvent) => {
             const bd = Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
             params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields
             initialize?.(bd);
-            Doc.GetProto(bd)["onClick-paramFieldKeys"] = new List<string>(params);
+            Doc.GetProto(bd)['onClick-paramFieldKeys'] = new List<string>(params);
             e.docDragData && (e.docDragData.droppedDocuments = [bd]);
             return e;
         };
         options = options ?? {};
-        options.noAutoscroll = true;  // these buttons are being dragged on the overlay layer, so scrollin the underlay is not appropriate
+        options.noAutoscroll = true; // these buttons are being dragged on the overlay layer, so scrollin the underlay is not appropriate
         StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
     }
 
-    // drag&drop the pdf annotation anchor which will create a text note  on drop via a dropCompleted() DragOption 
+    // drag&drop the pdf annotation anchor which will create a text note  on drop via a dropCompleted() DragOption
     export function StartAnchorAnnoDrag(eles: HTMLElement[], dragData: AnchorAnnoDragData, downX: number, downY: number, options?: DragOptions) {
         StartDrag(eles, dragData, downX, downY, options);
     }
@@ -286,8 +272,8 @@ export namespace DragManager {
         let near = dragPt;
         const intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, dragx: number, dragy: number) => {
             if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return undefined; // Check if none of the lines are of length 0
-            const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
-            if (denominator === 0) return undefined;  // Lines are parallel
+            const denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
+            if (denominator === 0) return undefined; // Lines are parallel
 
             const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
             // let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
@@ -315,14 +301,14 @@ export namespace DragManager {
         });
         return { x: near[0], y: near[1] };
     }
-    // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line 
+    // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line
     export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
         const snapThreshold = Utils.SNAP_THRESHOLD;
         const snapVal = (pts: number[], drag: number, snapLines: number[]) => {
             if (snapLines.length) {
-                const offs = [pts[0], (pts[0] - pts[1]) / 2, -pts[1]];   // offsets from drag pt
+                const offs = [pts[0], (pts[0] - pts[1]) / 2, -pts[1]]; // offsets from drag pt
                 const rangePts = [drag - offs[0], drag - offs[1], drag - offs[2]]; // left, mid, right or  top, mid, bottom pts to try to snap to snaplines
-                const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest));
+                const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => (Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest)));
                 const closestDists = rangePts.map((pt, i) => Math.abs(pt - closestPts[i]));
                 const minIndex = closestDists[0] < closestDists[1] && closestDists[0] < closestDists[2] ? 0 : closestDists[1] < closestDists[2] ? 1 : 2;
                 return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag;
@@ -331,55 +317,61 @@ export namespace DragManager {
         };
         return {
             x: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()),
-            y: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines())
+            y: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()),
         };
     }
     export let docsBeingDragged: Doc[] = [];
     export let CanEmbed = false;
     export let DocDragData: DocumentDragData | undefined;
     export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
-        if (dragData.dropAction === "none") return;
+        if (dragData.dropAction === 'none') return;
         DocDragData = dragData instanceof DocumentDragData ? dragData : undefined;
-        const batch = UndoManager.StartBatch("dragging");
+        const batch = UndoManager.StartBatch('dragging');
         eles = eles.filter(e => e);
         CanEmbed = dragData.canEmbed || false;
         if (!dragDiv) {
-            dragDiv = document.createElement("div");
-            dragDiv.className = "dragManager-dragDiv";
-            dragDiv.style.pointerEvents = "none";
-            dragLabel = document.createElement("div");
-            dragLabel.className = "dragManager-dragLabel";
-            dragLabel.style.zIndex = "100001";
-            dragLabel.style.fontSize = "10px";
-            dragLabel.style.position = "absolute";
-            dragLabel.innerText = "drag titlebar to embed on drop"; // bcz: need to move this to a status bar
+            dragDiv = document.createElement('div');
+            dragDiv.className = 'dragManager-dragDiv';
+            dragDiv.style.pointerEvents = 'none';
+            dragLabel = document.createElement('div');
+            dragLabel.className = 'dragManager-dragLabel';
+            dragLabel.style.zIndex = '100001';
+            dragLabel.style.fontSize = '10px';
+            dragLabel.style.position = 'absolute';
+            dragLabel.innerText = 'drag titlebar to embed on drop'; // bcz: need to move this to a status bar
             dragDiv.appendChild(dragLabel);
             DragManager.Root().appendChild(dragDiv);
         }
-        Object.assign(dragDiv.style, { width: "", height: "", overflow: "" });
+        Object.assign(dragDiv.style, { width: '', height: '', overflow: '' });
         dragDiv.hidden = false;
-        const scaleXs: number[] = [], scaleYs: number[] = [], xs: number[] = [], ys: number[] = [];
+        const scaleXs: number[] = [],
+            scaleYs: number[] = [],
+            xs: number[] = [],
+            ys: number[] = [];
 
         docsBeingDragged = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : [];
         const elesCont = {
-            left: Number.MAX_SAFE_INTEGER, right: Number.MIN_SAFE_INTEGER,
-            top: Number.MAX_SAFE_INTEGER, bottom: Number.MIN_SAFE_INTEGER
+            left: Number.MAX_SAFE_INTEGER,
+            right: Number.MIN_SAFE_INTEGER,
+            top: Number.MAX_SAFE_INTEGER,
+            bottom: Number.MIN_SAFE_INTEGER,
         };
         const dragElements = eles.map(ele => {
             if (!ele.parentNode) dragDiv.appendChild(ele);
-            const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
+            const dragElement = ele.parentNode === dragDiv ? ele : (ele.cloneNode(true) as HTMLElement);
             const children = Array.from(dragElement.children);
-            while (children.length) { // need to replace all the maker node reference ids with new unique ids. otherwise, the clone nodes will reference the original nodes which are all hidden during the drag
+            while (children.length) {
+                // need to replace all the maker node reference ids with new unique ids. otherwise, the clone nodes will reference the original nodes which are all hidden during the drag
                 const next = children.pop();
                 next && children.push(...Array.from(next.children));
                 if (next) {
-                    ["marker-start", "marker-mid", "marker-end"].forEach(field => {
-                        if (next.localName.startsWith("path") && (next.attributes as any)[field]) {
-                            next.setAttribute(field, (next.attributes as any)[field].value.replace("#", "#X"));
+                    ['marker-start', 'marker-mid', 'marker-end'].forEach(field => {
+                        if (next.localName.startsWith('path') && (next.attributes as any)[field]) {
+                            next.setAttribute(field, (next.attributes as any)[field].value.replace('#', '#X'));
                         }
                     });
-                    if (next.localName.startsWith("marker")) {
-                        next.id = "X" + next.id;
+                    if (next.localName.startsWith('marker')) {
+                        next.id = 'X' + next.id;
                     }
                 }
             }
@@ -396,20 +388,31 @@ export namespace DragManager {
             scaleXs.push(scaleX);
             scaleYs.push(scaleY);
             Object.assign(dragElement.style, {
-                opacity: "0.7", position: "absolute", margin: "0", top: "0", bottom: "", left: "0", color: "black", transition: "none",
-                borderRadius: getComputedStyle(ele).borderRadius, zIndex: globalCssVariables.contextMenuZindex,
-                transformOrigin: "0 0", width: `${rect.width / scaleX}px`, height: `${rect.height / scaleY}px`,
+                opacity: '0.7',
+                position: 'absolute',
+                margin: '0',
+                top: '0',
+                bottom: '',
+                left: '0',
+                color: 'black',
+                transition: 'none',
+                borderRadius: getComputedStyle(ele).borderRadius,
+                zIndex: globalCssVariables.contextMenuZindex,
+                transformOrigin: '0 0',
+                width: `${rect.width / scaleX}px`,
+                height: `${rect.height / scaleY}px`,
                 transform: `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`,
             });
             dragLabel.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0) - 20}px)`;
 
             if (docsBeingDragged.length) {
-                const pdfBox = dragElement.getElementsByTagName("canvas");
-                const pdfBoxSrc = ele.getElementsByTagName("canvas");
-                Array.from(pdfBox).filter(pb => pb.width && pb.height).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
+                const pdfBox = dragElement.getElementsByTagName('canvas');
+                const pdfBoxSrc = ele.getElementsByTagName('canvas');
+                Array.from(pdfBox)
+                    .filter(pb => pb.width && pb.height)
+                    .map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
             }
-            [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele =>
-                (ele as any).style && ((ele as any).style.pointerEvents = "none"));
+            [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => (ele as any).style && ((ele as any).style.pointerEvents = 'none'));
 
             dragDiv.appendChild(dragElement);
             if (dragElement !== ele) {
@@ -427,9 +430,9 @@ export namespace DragManager {
         });
 
         const hideDragShowOriginalElements = (hide: boolean) => {
-            dragLabel.style.display = hide ? "" : "none";
+            dragLabel.style.display = hide ? '' : 'none';
             !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
-            eles.forEach(ele => ele.hidden = hide);
+            eles.forEach(ele => (ele.hidden = hide));
         };
         options?.hideSource && hideDragShowOriginalElements(true);
 
@@ -448,8 +451,8 @@ export namespace DragManager {
 
         const cleanupDrag = action(() => {
             hideDragShowOriginalElements(false);
-            document.removeEventListener("pointermove", moveHandler, true);
-            document.removeEventListener("pointerup", upHandler);
+            document.removeEventListener('pointermove', moveHandler, true);
+            document.removeEventListener('pointerup', upHandler, true);
             SnappingManager.SetIsDragging(false);
             SnappingManager.clearSnapLines();
             batch.end();
@@ -458,17 +461,17 @@ 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 ? 'alias' : dragData.defaultDropAction;
             }
-            if (((e.target as any)?.className === "lm_tabs" || (e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
+            if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header' || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
                 if (!startWindowDragTimer) {
                     startWindowDragTimer = setTimeout(async () => {
                         startWindowDragTimer = undefined;
-                        dragData.dropAction = dragData.userDropAction || "same";
+                        dragData.dropAction = dragData.userDropAction || 'same';
                         AbortDrag();
                         await finishDrag?.(new DragCompleteEvent(true, dragData));
-                        DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => {
-                            if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) {
+                        DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, aborted => {
+                            if (!aborted && (dragData.dropAction === 'move' || dragData.dropAction === 'same')) {
                                 dragData.removeDocument?.(dragData.draggedDocuments[0]);
                             }
                         });
@@ -484,7 +487,7 @@ export namespace DragManager {
             if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._noAutoscroll)) {
                 const autoScrollHandler = () => {
                     target.dispatchEvent(
-                        new CustomEvent<React.DragEvent>("dashDragAutoScroll", {
+                        new CustomEvent<React.DragEvent>('dashDragAutoScroll', {
                             bubbles: true,
                             detail: {
                                 shiftKey: e.shiftKey,
@@ -493,7 +496,7 @@ export namespace DragManager {
                                 ctrlKey: e.ctrlKey,
                                 clientX: e.clientX,
                                 clientY: e.clientY,
-                                dataTransfer: new DataTransfer,
+                                dataTransfer: new DataTransfer(),
                                 button: e.button,
                                 buttons: e.buttons,
                                 getModifierState: e.getModifierState,
@@ -505,8 +508,8 @@ export namespace DragManager {
                                 screenX: e.screenX,
                                 screenY: e.screenY,
                                 detail: e.detail,
-                                view: e.view ? e.view : new Window as any,
-                                nativeEvent: new DragEvent("dashDragAutoScroll"),
+                                view: e.view ? e.view : (new Window() as any),
+                                nativeEvent: new DragEvent('dashDragAutoScroll'),
                                 currentTarget: target,
                                 target: target,
                                 bubbles: true,
@@ -514,14 +517,14 @@ export namespace DragManager {
                                 defaultPrevented: true,
                                 eventPhase: e.eventPhase,
                                 isTrusted: true,
-                                preventDefault: () => "not implemented for this event" ? false : false,
-                                isDefaultPrevented: () => "not implemented for this event" ? false : false,
-                                stopPropagation: () => "not implemented for this event" ? false : false,
-                                isPropagationStopped: () => "not implemented for this event" ? false : false,
+                                preventDefault: () => ('not implemented for this event' ? false : false),
+                                isDefaultPrevented: () => ('not implemented for this event' ? false : false),
+                                stopPropagation: () => ('not implemented for this event' ? false : false),
+                                isPropagationStopped: () => ('not implemented for this event' ? false : false),
                                 persist: emptyFunction,
                                 timeStamp: e.timeStamp,
-                                type: "dashDragAutoScroll"
-                            }
+                                type: 'dashDragAutoScroll',
+                            },
                         })
                     );
 
@@ -537,20 +540,18 @@ export namespace DragManager {
             lastPt = { x, y };
 
             dragLabel.style.transform = `translate(${xs[0] + moveVec.x + (options?.offsetX || 0)}px, ${ys[0] + moveVec.y + (options?.offsetY || 0) - 20}px)`;
-            dragElements.map((dragElement, i) => (dragElement.style.transform =
-                `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px)  scale(${scaleXs[i]}, ${scaleYs[i]})`)
-            );
+            dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px)  scale(${scaleXs[i]}, ${scaleYs[i]})`));
         };
         const upHandler = (e: PointerEvent) => {
             clearTimeout(startWindowDragTimer);
             startWindowDragTimer = undefined;
             dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, cleanupDrag);
         };
-        document.addEventListener("pointermove", moveHandler, true);
-        document.addEventListener("pointerup", upHandler);
+        document.addEventListener('pointermove', moveHandler, true);
+        document.addEventListener('pointerup', upHandler, true);
     }
 
-    async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
+    async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
         const dropArgs = {
             bubbles: true,
             detail: {
@@ -560,13 +561,13 @@ export namespace DragManager {
                 altKey: e.altKey,
                 metaKey: e.metaKey,
                 ctrlKey: e.ctrlKey,
-                embedKey: CanEmbed
-            }
+                embedKey: CanEmbed,
+            },
         };
-        target.dispatchEvent(new CustomEvent<DropEvent>("dashPreDrop", dropArgs));
+        target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
         await finishDrag?.(complete);
-        target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", dropArgs));
+        target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs));
         options?.dragComplete?.(complete);
         endDrag?.();
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 1a2054676..bd5e8caab 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -219,8 +219,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
             const doc = this._itemRef.current || dragArray[0];
-            doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
-            dragItem.push(doc);
+            if (doc) {
+                doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
+                dragItem.push(doc);
+            }
         } else if (dragArray.length >= 1) {
             const doc = document.createElement('div');
             doc.className = 'presItem-multiDrag';
-- 
cgit v1.2.3-70-g09d2


From 358f9e266ef264442aea1e2c7d5d959a19f7624c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 22 Jul 2022 14:11:30 -0400
Subject: adjusted native dim scaling slightly combining props.scaling and
 props.ContentScaling into props.NativeDimScaling and fixing some resizing
 behaviors for fitWidth freeformviews and native-sized text boxes.   Also
 fixed clicking on presboxe elements to not drag.

---
 src/client/views/DocumentDecorations.tsx           |   3 +-
 src/client/views/InkingStroke.tsx                  | 536 +++++++++++++--------
 src/client/views/SidebarAnnos.tsx                  |   4 +-
 src/client/views/StyleProvider.tsx                 |   4 +-
 .../collections/CollectionStackedTimeline.tsx      |   2 +-
 .../views/collections/CollectionTreeView.tsx       |  11 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  30 +-
 .../CollectionMulticolumnView.tsx                  | 187 +++----
 .../CollectionMultirowView.tsx                     | 180 +++----
 src/client/views/nodes/DocumentContentsView.tsx    |   5 +-
 src/client/views/nodes/DocumentLinksButton.tsx     | 384 ++++++++-------
 src/client/views/nodes/DocumentView.tsx            |  24 +-
 src/client/views/nodes/EquationBox.tsx             |  90 ++--
 src/client/views/nodes/FieldView.tsx               |  55 ++-
 src/client/views/nodes/ImageBox.tsx                |  10 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   9 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |   2 +-
 src/client/views/nodes/PDFBox.tsx                  |  12 +-
 src/client/views/nodes/ScreenshotBox.tsx           |   4 +-
 src/client/views/nodes/VideoBox.tsx                |   6 +-
 src/client/views/nodes/WebBox.tsx                  |  25 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  10 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |   8 +-
 src/client/views/pdf/PDFViewer.tsx                 |  17 +-
 25 files changed, 883 insertions(+), 741 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 780dcfb6d..964fd36c8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -521,7 +521,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             } else doc._height = actualdH;
                         }
                     } else {
-                        dH && (doc._height = actualdH);
+                        const maxHeight = Math.max(nheight, NumCast(doc.scrollHeight)) * docView.NativeDimScaling();
+                        dH && (doc._height = actualdH > maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
                         dH && (doc._autoHeight = false);
                     }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index dace9716a..e5de7a0c5 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -20,35 +20,37 @@
     
     Most of the operations that can be performed on an InkStroke (eg delete a point, rotate, stretch) are implemented in the InkStrokeProperties helper class
 */
-import React = require("react");
-import { action, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, WidthSym } from "../../fields/Doc";
-import { InkData, InkField, InkTool } from "../../fields/InkField";
-import { Cast, NumCast, RTFCast, StrCast } from "../../fields/Types";
-import { TraceMobx } from "../../fields/util";
-import { OmitKeys, returnFalse, setupMoveUpEvents } from "../../Utils";
-import { CognitiveServices } from "../cognitive_services/CognitiveServices";
-import { InteractionUtils } from "../util/InteractionUtils";
-import { SnappingManager } from "../util/SnappingManager";
-import { Transform } from "../util/Transform";
-import { UndoManager } from "../util/UndoManager";
-import { ContextMenu } from "./ContextMenu";
-import { ViewBoxBaseComponent } from "./DocComponent";
-import { Colors } from "./global/globalEnums";
-import { InkControlPtHandles, InkEndPtHandles } from "./InkControlPtHandles";
-import { InkStrokeProperties } from "./InkStrokeProperties";
-import { InkTangentHandles } from "./InkTangentHandles";
-import { DocComponentView } from "./nodes/DocumentView";
-import { FieldView, FieldViewProps } from "./nodes/FieldView";
-import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
-import "./InkStroke.scss";
-import Color = require("color");
+import React = require('react');
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, WidthSym } from '../../fields/Doc';
+import { InkData, InkField, InkTool } from '../../fields/InkField';
+import { Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
+import { TraceMobx } from '../../fields/util';
+import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils';
+import { CognitiveServices } from '../cognitive_services/CognitiveServices';
+import { InteractionUtils } from '../util/InteractionUtils';
+import { SnappingManager } from '../util/SnappingManager';
+import { Transform } from '../util/Transform';
+import { UndoManager } from '../util/UndoManager';
+import { ContextMenu } from './ContextMenu';
+import { ViewBoxBaseComponent } from './DocComponent';
+import { Colors } from './global/globalEnums';
+import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
+import { InkStrokeProperties } from './InkStrokeProperties';
+import { InkTangentHandles } from './InkTangentHandles';
+import { DocComponentView } from './nodes/DocumentView';
+import { FieldView, FieldViewProps } from './nodes/FieldView';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import './InkStroke.scss';
+import Color = require('color');
 
 @observer
 export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     static readonly MaskDim = 50000; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big)
-    public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
+    public static LayoutString(fieldStr: string) {
+        return FieldView.LayoutString(InkingStroke, fieldStr);
+    }
     public static IsClosed(inkData: InkData) {
         return inkData && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y;
     }
@@ -56,13 +58,15 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     private _selDisposer?: IReactionDisposer;
 
     @observable _nearestSeg?: number; // nearest Bezier segment along the ink stroke to the cursor (used for displaying the Add Point highlight)
-    @observable _nearestT?: number;  // nearest t value within the nearest Bezier segment "
-    @observable _nearestScrPt?: { X: number, Y: number }; // nearst screen point on the ink stroke ""
+    @observable _nearestT?: number; // nearest t value within the nearest Bezier segment "
+    @observable _nearestScrPt?: { X: number; Y: number }; // nearst screen point on the ink stroke ""
 
     componentDidMount() {
         this.props.setContentView?.(this);
-        this._selDisposer = reaction(() => this.props.isSelected(), // react to stroke being deselected by turning off ink handles
-            selected => !selected && (InkStrokeProperties.Instance._controlButton = false));
+        this._selDisposer = reaction(
+            () => this.props.isSelected(), // react to stroke being deselected by turning off ink handles
+            selected => !selected && (InkStrokeProperties.Instance._controlButton = false)
+        );
     }
     componentWillUnmount() {
         this._selDisposer?.();
@@ -70,36 +74,36 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
-    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
+    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.NativeDimScaling?.() || 1);
 
     getAnchor = () => {
         console.log(document.activeElement);
         return this._subContentView?.getAnchor?.() || this.rootDoc;
-    }
+    };
 
     scrollFocus = (textAnchor: Doc, smooth: boolean) => {
         return this._subContentView?.scrollFocus?.(textAnchor, smooth);
-    }
+    };
     /**
-    * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space); 
-    * DocumentDecorations calls getBounds() on DocumentViews which call getCenter() if defined - in the case of ink it needs to be defined since
-    * the center of the ink stroke changes as the stroke is rotated.
-    */
+     * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space);
+     * DocumentDecorations calls getBounds() on DocumentViews which call getCenter() if defined - in the case of ink it needs to be defined since
+     * the center of the ink stroke changes as the stroke is rotated.
+     */
     getCenter = (xf: Transform) => {
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const angle = -NumCast(this.layoutDoc.rotation);
         const newPoints = inkData.map(pt => {
-            const newX = Math.cos(angle) * pt.X - Math.sin(angle) * pt.Y * inkScaleY / inkScaleX;
-            const newY = Math.sin(angle) * pt.X * inkScaleX / inkScaleY + Math.cos(angle) * pt.Y;
+            const newX = Math.cos(angle) * pt.X - (Math.sin(angle) * pt.Y * inkScaleY) / inkScaleX;
+            const newY = (Math.sin(angle) * pt.X * inkScaleX) / inkScaleY + Math.cos(angle) * pt.Y;
             return { X: newX, Y: newY };
         });
         const crx = (Math.max(...newPoints.map(np => np.X)) + Math.min(...newPoints.map(np => np.X))) / 2;
         const cry = (Math.max(...newPoints.map(np => np.Y)) + Math.min(...newPoints.map(np => np.Y))) / 2;
-        const cx = Math.cos(-angle) * crx - Math.sin(-angle) * cry * inkScaleY / inkScaleX;
-        const cy = Math.sin(-angle) * crx * inkScaleX / inkScaleY + Math.cos(-angle) * cry;
+        const cx = Math.cos(-angle) * crx - (Math.sin(-angle) * cry * inkScaleY) / inkScaleX;
+        const cy = (Math.sin(-angle) * crx * inkScaleX) / inkScaleY + Math.cos(-angle) * cry;
         const tc = xf.transformPoint((cx - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (cy - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2);
         return { X: tc[0], Y: tc[1] };
-    }
+    };
 
     /**
      * analyzes the ink stroke and saves the analysis of the stroke to the 'inkAnalysis' field,
@@ -107,7 +111,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     analyzeStrokes() {
         const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
-        CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]);
+        CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ['inkAnalysis', 'handwriting'], [data]);
     }
 
     /**
@@ -115,12 +119,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
      *    When displayed as a mask, the stroke is rendered with mixBlendMode set to multiply so that the stroke will
      *  appear to illuminate what it covers up.  At the same time, all pixels that are not under the stroke will be
      *  dimmed by a semi-opaque overlay mask.
-    */
+     */
     public static toggleMask = action((inkDoc: Doc) => {
         inkDoc.isInkMask = !inkDoc.isInkMask;
-        inkDoc._backgroundColor = inkDoc.isInkMask ? "rgba(0,0,0,0.7)" : undefined;
-        inkDoc.mixBlendMode = inkDoc.isInkMask ? "hard-light" : undefined;
-        inkDoc.color = "#9b9b9bff";
+        inkDoc._backgroundColor = inkDoc.isInkMask ? 'rgba(0,0,0,0.7)' : undefined;
+        inkDoc.mixBlendMode = inkDoc.isInkMask ? 'hard-light' : undefined;
+        inkDoc.color = '#9b9b9bff';
         inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined;
     });
     /**
@@ -132,46 +136,60 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         this._handledClick = false;
         const inkView = this.props.docViewPath().lastElement();
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
-        const screenPts = inkData.map(point => this.screenToLocal().inverse().transformPoint(
-            (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
+        const screenPts = inkData
+            .map(point =>
+                this.screenToLocal()
+                    .inverse()
+                    .transformPoint((point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)
+            )
+            .map(p => ({ X: p[0], Y: p[1] }));
         const { nearestSeg } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
         const controlIndex = nearestSeg;
         const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex;
         var controlUndo: UndoManager.Batch | undefined;
         const isEditing = InkStrokeProperties.Instance._controlButton && this.props.isSelected();
-        setupMoveUpEvents(this, e,
-            !isEditing ? returnFalse : action((e: PointerEvent, down: number[], delta: number[]) => {
-                if (!controlUndo) controlUndo = UndoManager.StartBatch("drag ink ctrl pt");
-                const inkMoveEnd = this.ptFromScreen({ X: delta[0], Y: delta[1] });
-                const inkMoveStart = this.ptFromScreen({ X: 0, Y: 0 });
-                InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex);
-                InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex + 3);
-                return false;
-            }),
-            !isEditing ? returnFalse : action(() => {
-                controlUndo?.end();
-                controlUndo = undefined;
-                UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
-            }),
+        setupMoveUpEvents(
+            this,
+            e,
+            !isEditing
+                ? returnFalse
+                : action((e: PointerEvent, down: number[], delta: number[]) => {
+                      if (!controlUndo) controlUndo = UndoManager.StartBatch('drag ink ctrl pt');
+                      const inkMoveEnd = this.ptFromScreen({ X: delta[0], Y: delta[1] });
+                      const inkMoveStart = this.ptFromScreen({ X: 0, Y: 0 });
+                      InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex);
+                      InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex + 3);
+                      return false;
+                  }),
+            !isEditing
+                ? returnFalse
+                : action(() => {
+                      controlUndo?.end();
+                      controlUndo = undefined;
+                      UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
+                  }),
             action((e: PointerEvent, doubleTap: boolean | undefined) => {
                 doubleTap = doubleTap || this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick;
                 if (doubleTap) {
                     InkStrokeProperties.Instance._controlButton = true;
                     InkStrokeProperties.Instance._currentPoint = -1;
-                    this._handledClick = true;  // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
+                    this._handledClick = true; // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
                     if (isEditing) {
                         this._nearestT && this._nearestSeg !== undefined && InkStrokeProperties.Instance.addPoints(this.props.docViewPath().lastElement(), this._nearestT, this._nearestSeg, this.inkScaledData().inkData.slice());
                     }
                 }
-            }), isEditing, isEditing, action(() => wasSelected && (InkStrokeProperties.Instance._currentPoint = -1)));
-    }
+            }),
+            isEditing,
+            isEditing,
+            action(() => wasSelected && (InkStrokeProperties.Instance._currentPoint = -1))
+        );
+    };
 
     /**
      * @param scrPt a point in the screen coordinate space
      * @returns the point in the ink data's coordinate space.
      */
-    ptFromScreen = (scrPt: { X: number, Y: number }) => {
+    ptFromScreen = (scrPt: { X: number; Y: number }) => {
         const { inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const docPt = this.screenToLocal().transformPoint(scrPt.X, scrPt.Y);
         const inkPt = {
@@ -179,39 +197,39 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             Y: (docPt[1] - inkStrokeWidth / 2) / inkScaleY + inkStrokeWidth / 2 + inkTop,
         };
         return inkPt;
-    }
+    };
 
     /**
      * @param inkPt a point in the ink data's coordinate space
      * @returns the screen point corresponding to the ink point
      */
-    ptToScreen = (inkPt: { X: number, Y: number }) => {
+    ptToScreen = (inkPt: { X: number; Y: number }) => {
         const { inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
         const docPt = {
             X: (inkPt.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            Y: (inkPt.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2
+            Y: (inkPt.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2,
         };
         const scrPt = this.screenToLocal().inverse().transformPoint(docPt.X, docPt.Y);
         return { X: scrPt[0], Y: scrPt[1] };
-    }
+    };
 
     /**
-    * Snaps a screen space point to this stroke, optionally skipping bezier segments indicated by 'excludeSegs'
-    * @param scrPt - the point to snap to this stroke
-    * @param excludeSegs - optional segments in this stroke to skip (this is used when dragging a point on the stroke and not wanting the drag point to snap to its neighboring segments)
-    *
-    * @returns the nearest ink space point on this stroke to the screen point AND the screen space distance from the snapped point to the nearest point
-    */
-    snapPt = (scrPt: { X: number, Y: number }, excludeSegs?: number[]) => {
+     * Snaps a screen space point to this stroke, optionally skipping bezier segments indicated by 'excludeSegs'
+     * @param scrPt - the point to snap to this stroke
+     * @param excludeSegs - optional segments in this stroke to skip (this is used when dragging a point on the stroke and not wanting the drag point to snap to its neighboring segments)
+     *
+     * @returns the nearest ink space point on this stroke to the screen point AND the screen space distance from the snapped point to the nearest point
+     */
+    snapPt = (scrPt: { X: number; Y: number }, excludeSegs?: number[]) => {
         const { inkData } = this.inkScaledData();
         const { nearestPt, distance } = InkStrokeProperties.nearestPtToStroke(inkData, this.ptFromScreen(scrPt), excludeSegs ?? []);
         return { nearestPt, distance: distance * this.screenToLocal().inverse().Scale };
-    }
+    };
 
     /**
-    * extracts key features from the inkData, including: the data points, the ink width, the ink bounds (top,left, width, height), and the scale
-    * factor for converting between ink and screen space.
-    */
+     * extracts key features from the inkData, including: the data points, the ink width, the ink bounds (top,left, width, height), and the scale
+     * factor for converting between ink and screen space.
+     */
     inkScaledData = () => {
         const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
         const inkStrokeWidth = NumCast(this.rootDoc.strokeWidth, 1);
@@ -228,27 +246,31 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkLeft,
             inkWidth,
             inkHeight,
-            inkScaleX: ((this.props.PanelWidth() - inkStrokeWidth) / ((inkWidth - inkStrokeWidth) || 1) || 1),
-            inkScaleY: ((this.props.PanelHeight() - inkStrokeWidth) / ((inkHeight - inkStrokeWidth) || 1) || 1)
+            inkScaleX: (this.props.PanelWidth() - inkStrokeWidth) / (inkWidth - inkStrokeWidth || 1) || 1,
+            inkScaleY: (this.props.PanelHeight() - inkStrokeWidth) / (inkHeight - inkStrokeWidth || 1) || 1,
         };
-    }
+    };
 
     //
-    // this updates the highlight for the nearest point on the curve to the cursor.  
+    // this updates the highlight for the nearest point on the curve to the cursor.
     // if the user double clicks, this highlighted point will be added as a control point in the curve.
     //
     @action
     onPointerMove = (e: React.PointerEvent) => {
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
-        const screenPts = inkData.map(point => this.screenToLocal().inverse().transformPoint(
-            (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
+        const screenPts = inkData
+            .map(point =>
+                this.screenToLocal()
+                    .inverse()
+                    .transformPoint((point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)
+            )
+            .map(p => ({ X: p[0], Y: p[1] }));
         const { distance, nearestT, nearestSeg, nearestPt } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
 
         this._nearestT = nearestT;
         this._nearestSeg = nearestSeg;
         this._nearestScrPt = nearestPt;
-    }
+    };
 
     /**
      * @returns the nearest screen point to the cursor (to render a highlight for the point to be added)
@@ -263,50 +285,66 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     componentUI = (boundsLeft: number, boundsTop: number) => {
         const inkDoc = this.props.Document;
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
-        const screenSpaceCenterlineStrokeWidth = Math.min(3, inkStrokeWidth * this.screenToLocal().inverse().Scale);  // the width of the blue line widget that shows the centerline of the ink stroke
+        const screenSpaceCenterlineStrokeWidth = Math.min(3, inkStrokeWidth * this.screenToLocal().inverse().Scale); // the width of the blue line widget that shows the centerline of the ink stroke
 
         const screenInkWidth = this.screenToLocal().inverse().transformDirection(inkStrokeWidth, inkStrokeWidth);
-        const screenPts = inkData.map(point => this.screenToLocal().inverse().transformPoint(
-            (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
-            (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
+        const screenPts = inkData
+            .map(point =>
+                this.screenToLocal()
+                    .inverse()
+                    .transformPoint((point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)
+            )
+            .map(p => ({ X: p[0], Y: p[1] }));
         const screenHdlPts = screenPts;
 
         const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
         const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale);
-        return SnappingManager.GetIsDragging() ? (null) :
-            !InkStrokeProperties.Instance._controlButton ?
-                (!this.props.isSelected() || InkingStroke.IsClosed(inkData) ? (null) :
-                    <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-                        <InkEndPtHandles
-                            inkView={this.props.docViewPath().lastElement()}
-                            inkDoc={inkDoc}
-                            startPt={screenPts[0]}
-                            endPt={screenPts.lastElement()}
-                            screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
-                    </div>) :
+        return SnappingManager.GetIsDragging() ? null : !InkStrokeProperties.Instance._controlButton ? (
+            !this.props.isSelected() || InkingStroke.IsClosed(inkData) ? null : (
                 <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-                    {InteractionUtils.CreatePolyline(screenPts, 0, 0, Colors.MEDIUM_BLUE, screenInkWidth[0], screenSpaceCenterlineStrokeWidth,
-                        StrCast(inkDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), StrCast(inkDoc.strokeBezier),
-                        "none", startMarker, endMarker, markerScale * Math.min(screenSpaceCenterlineStrokeWidth, screenInkWidth[0] / screenSpaceCenterlineStrokeWidth), StrCast(inkDoc.strokeDash), 1, 1, "", "none", 1.0, false)}
-                    <InkControlPtHandles
-                        inkView={this.props.docViewPath().lastElement()}
-                        inkDoc={inkDoc}
-                        inkCtrlPoints={inkData}
-                        screenCtrlPoints={screenHdlPts}
-                        nearestScreenPt={this.nearestScreenPt}
-                        screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
-                    <InkTangentHandles
-                        inkView={this.props.docViewPath().lastElement()}
-                        inkDoc={inkDoc}
-                        screenCtrlPoints={screenHdlPts}
-                        screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth}
-                        ScreenToLocalTransform={this.screenToLocal} />
-                </div>;
-    }
+                    <InkEndPtHandles inkView={this.props.docViewPath().lastElement()} inkDoc={inkDoc} startPt={screenPts[0]} endPt={screenPts.lastElement()} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
+                </div>
+            )
+        ) : (
+            <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+                {InteractionUtils.CreatePolyline(
+                    screenPts,
+                    0,
+                    0,
+                    Colors.MEDIUM_BLUE,
+                    screenInkWidth[0],
+                    screenSpaceCenterlineStrokeWidth,
+                    StrCast(inkDoc.strokeLineJoin),
+                    StrCast(this.layoutDoc.strokeLineCap),
+                    StrCast(inkDoc.strokeBezier),
+                    'none',
+                    startMarker,
+                    endMarker,
+                    markerScale * Math.min(screenSpaceCenterlineStrokeWidth, screenInkWidth[0] / screenSpaceCenterlineStrokeWidth),
+                    StrCast(inkDoc.strokeDash),
+                    1,
+                    1,
+                    '',
+                    'none',
+                    1.0,
+                    false
+                )}
+                <InkControlPtHandles
+                    inkView={this.props.docViewPath().lastElement()}
+                    inkDoc={inkDoc}
+                    inkCtrlPoints={inkData}
+                    screenCtrlPoints={screenHdlPts}
+                    nearestScreenPt={this.nearestScreenPt}
+                    screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth}
+                />
+                <InkTangentHandles inkView={this.props.docViewPath().lastElement()} inkDoc={inkDoc} screenCtrlPoints={screenHdlPts} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} ScreenToLocalTransform={this.screenToLocal} />
+            </div>
+        );
+    };
 
     _subContentView: DocComponentView | undefined;
-    setSubContentView = (doc: DocComponentView) => this._subContentView = doc;
+    setSubContentView = (doc: DocComponentView) => (this._subContentView = doc);
     render() {
         TraceMobx();
         const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData();
@@ -315,105 +353,181 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
         const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
         const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1);
         const closed = InkingStroke.IsClosed(inkData);
-        const fillColor = StrCast(this.layoutDoc.fillColor, "transparent");
-        const strokeColor = !closed && fillColor && fillColor !== "transparent" ? fillColor : StrCast(this.layoutDoc.color);
+        const fillColor = StrCast(this.layoutDoc.fillColor, 'transparent');
+        const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : StrCast(this.layoutDoc.color);
 
         // Visually renders the polygonal line made by the user.
-        const inkLine = InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, strokeColor, inkStrokeWidth, inkStrokeWidth,
-            StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
-            StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" ? "none" : fillColor, startMarker, endMarker,
-            markerScale, StrCast(this.layoutDoc.strokeDash), inkScaleX, inkScaleY, "", "none", 1.0, false);
+        const inkLine = InteractionUtils.CreatePolyline(
+            inkData,
+            inkLeft,
+            inkTop,
+            strokeColor,
+            inkStrokeWidth,
+            inkStrokeWidth,
+            StrCast(this.layoutDoc.strokeLineJoin),
+            StrCast(this.layoutDoc.strokeLineCap),
+            StrCast(this.layoutDoc.strokeBezier),
+            !closed ? 'none' : fillColor === 'transparent' ? 'none' : fillColor,
+            startMarker,
+            endMarker,
+            markerScale,
+            StrCast(this.layoutDoc.strokeDash),
+            inkScaleX,
+            inkScaleY,
+            '',
+            'none',
+            1.0,
+            false
+        );
         const highlightIndex = /*BoolCast(this.props.Document.isLinkButton) && */ Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = !highlightIndex ?
-            StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== "transparent" ? StrCast(this.layoutDoc.color, "transparent") : "transparent") :
-            ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "yellow", "magenta", "cyan", "orange"][highlightIndex];
+        const highlightColor = !highlightIndex
+            ? StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent')
+            : ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'yellow', 'magenta', 'cyan', 'orange'][highlightIndex];
         // Invisible polygonal line that enables the ink to be selected by the user.
-        const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, suppressFill: boolean = false) => InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, highlightColor,
-            inkStrokeWidth, fillColor && closed && highlightIndex ? highlightIndex / 2 : inkStrokeWidth + (fillColor ? closed ? 0 : (highlightIndex + 2) : 0),
-            StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
-            StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" || suppressFill ? "none" : fillColor, startMarker, endMarker,
-            markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? "none" : "visiblepainted"), 0.0,
-            false, downHdlr);
-        const fsize = +(StrCast(this.props.Document.fontSize, "12px").replace("px", ""));
-        // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size. 
+        const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, suppressFill: boolean = false) =>
+            InteractionUtils.CreatePolyline(
+                inkData,
+                inkLeft,
+                inkTop,
+                highlightColor,
+                inkStrokeWidth,
+                fillColor && closed && highlightIndex ? highlightIndex / 2 : inkStrokeWidth + (fillColor ? (closed ? 0 : highlightIndex + 2) : 0),
+                StrCast(this.layoutDoc.strokeLineJoin),
+                StrCast(this.layoutDoc.strokeLineCap),
+                StrCast(this.layoutDoc.strokeBezier),
+                !closed ? 'none' : fillColor === 'transparent' || suppressFill ? 'none' : fillColor,
+                startMarker,
+                endMarker,
+                markerScale,
+                undefined,
+                inkScaleX,
+                inkScaleY,
+                '',
+                this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? 'none' : 'visiblepainted'),
+                0.0,
+                false,
+                downHdlr
+            );
+        const fsize = +StrCast(this.props.Document.fontSize, '12px').replace('px', '');
+        // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size.
         // this attempts to figure out the lineHeight ratio by inquiring the body's lineHeight and dividing by the fontsize which should yield 1.428571429
         // see: https://bibwild.wordpress.com/2019/06/10/bootstrap-3-to-4-changes-in-how-font-size-line-height-and-spacing-is-done-or-what-happened-to-line-height-computed/
-        const lineHeightGuess = (+getComputedStyle(document.body).lineHeight.replace("px", "")) / (+getComputedStyle(document.body).fontSize.replace("px", ""));
+        const lineHeightGuess = +getComputedStyle(document.body).lineHeight.replace('px', '') / +getComputedStyle(document.body).fontSize.replace('px', '');
         const interactions = {
-            onPointerLeave: action(() => this._nearestScrPt = undefined),
+            onPointerLeave: action(() => (this._nearestScrPt = undefined)),
             onPointerMove: this.props.isSelected() ? this.onPointerMove : undefined,
             onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(),
             onContextMenu: () => {
                 const cm = ContextMenu.Instance;
-                !Doc.noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
-                cm?.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" });
-                cm?.addItem({ description: "Edit Points", event: action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton), icon: "paint-brush" });
-            }
+                !Doc.noviceMode && cm?.addItem({ description: 'Recognize Writing', event: this.analyzeStrokes, icon: 'paint-brush' });
+                cm?.addItem({ description: 'Toggle Mask', event: () => InkingStroke.toggleMask(this.rootDoc), icon: 'paint-brush' });
+                cm?.addItem({ description: 'Edit Points', event: action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton)), icon: 'paint-brush' });
+            },
         };
-        return <div className="inkStroke-wrapper">
-            <svg className="inkStroke"
-                style={{
-                    transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
-                    mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
-                    cursor: this.props.isSelected() ? "default" : undefined
-                }}
-                {...(!closed ? interactions : {})}
-            >
-                {closed ? inkLine : clickableLine(this.onPointerDown)}
-                {closed ? clickableLine(this.onPointerDown) : inkLine}
-            </svg>
-            {!closed || (!RTFCast(this.rootDoc.text)?.Text && !this.props.isSelected()) ? (null) :
-                <div className="inkStroke-text" style={{
-                    color: StrCast(this.layoutDoc.textColor, "black"),
-                    pointerEvents: this.props.isDocumentActive?.() ? "all" : undefined,
-                    width: this.layoutDoc[WidthSym](),
-                    transform: `scale(${this.props.scaling?.() || 1})`,
-                    transformOrigin: "top left",
-                    top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.scaling?.() || 1)) / 2
-                }}>
-                    <FormattedTextBox
-                        {...OmitKeys(this.props, ['children']).omit}
-                        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"}
-                        fontSize={fsize}
-                        dontRegisterView={true}
-                        noSidebar={true}
-                        dontScale={true}
-                        isContentActive={this.isContentActive}
-                    />
-                </div>
-            }
-            {!closed ? null : <svg className="inkStroke"
-                style={{
-                    transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
-                    mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
-                    cursor: this.props.isSelected() ? "default" : undefined, position: "absolute"
-                }}
-                {...interactions}
-            >
-                {clickableLine(this.onPointerDown, true)}
-            </svg>}
-        </div>;
+        return (
+            <div className="inkStroke-wrapper">
+                <svg
+                    className="inkStroke"
+                    style={{
+                        transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                        mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
+                        cursor: this.props.isSelected() ? 'default' : undefined,
+                    }}
+                    {...(!closed ? interactions : {})}>
+                    {closed ? inkLine : clickableLine(this.onPointerDown)}
+                    {closed ? clickableLine(this.onPointerDown) : inkLine}
+                </svg>
+                {!closed || (!RTFCast(this.rootDoc.text)?.Text && !this.props.isSelected()) ? null : (
+                    <div
+                        className="inkStroke-text"
+                        style={{
+                            color: StrCast(this.layoutDoc.textColor, 'black'),
+                            pointerEvents: this.props.isDocumentActive?.() ? 'all' : undefined,
+                            width: this.layoutDoc[WidthSym](),
+                            transform: `scale(${this.props.NativeDimScaling?.() || 1})`,
+                            transformOrigin: 'top left',
+                            top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.NativeDimScaling?.() || 1)) / 2,
+                        }}>
+                        <FormattedTextBox
+                            {...OmitKeys(this.props, ['children']).omit}
+                            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'}
+                            fontSize={fsize}
+                            dontRegisterView={true}
+                            noSidebar={true}
+                            dontScale={true}
+                            isContentActive={this.isContentActive}
+                        />
+                    </div>
+                )}
+                {!closed ? null : (
+                    <svg
+                        className="inkStroke"
+                        style={{
+                            transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
+                            mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
+                            cursor: this.props.isSelected() ? 'default' : undefined,
+                            position: 'absolute',
+                        }}
+                        {...interactions}>
+                        {clickableLine(this.onPointerDown, true)}
+                    </svg>
+                )}
+            </div>
+        );
     }
 }
 
-
-export function SetActiveInkWidth(width: string): void { !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); }
-export function SetActiveBezierApprox(bezier: string): void { ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? "" : bezier); }
-export function SetActiveInkColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeInkColor = value); }
-export function SetActiveFillColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeFillColor = value); }
-export function SetActiveArrowStart(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); }
-export function SetActiveArrowEnd(value: string) { ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); }
-export function SetActiveArrowScale(value: number) { ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); }
-export function SetActiveDash(dash: string): void { !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); }
-export function ActiveInkPen(): Doc { return Doc.UserDoc(); }
-export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, "black"); }
-export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ""); }
-export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); }
-export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); }
-export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); }
-export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); }
-export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); }
-export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); }
+export function SetActiveInkWidth(width: string): void {
+    !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width);
+}
+export function SetActiveBezierApprox(bezier: string): void {
+    ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier);
+}
+export function SetActiveInkColor(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeInkColor = value);
+}
+export function SetActiveFillColor(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeFillColor = value);
+}
+export function SetActiveArrowStart(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeArrowStart = value);
+}
+export function SetActiveArrowEnd(value: string) {
+    ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value);
+}
+export function SetActiveArrowScale(value: number) {
+    ActiveInkPen() && (ActiveInkPen().activeArrowScale = value);
+}
+export function SetActiveDash(dash: string): void {
+    !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash);
+}
+export function ActiveInkPen(): Doc {
+    return Doc.UserDoc();
+}
+export function ActiveInkColor(): string {
+    return StrCast(ActiveInkPen()?.activeInkColor, 'black');
+}
+export function ActiveFillColor(): string {
+    return StrCast(ActiveInkPen()?.activeFillColor, '');
+}
+export function ActiveArrowStart(): string {
+    return StrCast(ActiveInkPen()?.activeArrowStart, '');
+}
+export function ActiveArrowEnd(): string {
+    return StrCast(ActiveInkPen()?.activeArrowEnd, '');
+}
+export function ActiveArrowScale(): number {
+    return NumCast(ActiveInkPen()?.activeArrowScale, 1);
+}
+export function ActiveDash(): string {
+    return StrCast(ActiveInkPen()?.activeDash, '0');
+}
+export function ActiveInkWidth(): number {
+    return Number(ActiveInkPen()?.activeInkWidth);
+}
+export function ActiveInkBezierApprox(): string {
+    return StrCast(ActiveInkPen()?.activeInkBezier);
+}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index e81a9c40f..9fdf9d2be 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -93,7 +93,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         this.props
             .ScreenToLocalTransform()
             .translate(Doc.NativeWidth(this.props.dataDoc), 0)
-            .scale(this.props.scaling?.() || 1);
+            .scale(this.props.NativeDimScaling?.() || 1);
     // panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 :
     //     this.props.usePanelWidth ? this.props.PanelWidth() :
     //         (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth);
@@ -164,7 +164,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
-                        scaling={returnOne}
+                        NativeDimScaling={returnOne}
                         childShowTitle={this.showTitle}
                         childDocumentsActive={this.props.isContentActive}
                         whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 340a5df45..334f381be 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -250,9 +250,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     return doc.z
                         ? `#9c9396  ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow
                         : props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK
-                        ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 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 && doc.type !== DocumentType.INK
-                        ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
+                        ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent
                         : isBackground()
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
                         : StrCast(doc.boxShadow, '');
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index f1d842386..714d8421a 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -519,7 +519,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                     isAnnotationOverlay={true}
                     isDocumentActive={returnFalse}
                     select={emptyFunction}
-                    scaling={returnOne}
+                    NativeDimScaling={returnOne}
                     xMargin={25}
                     yMargin={10}
                     ScreenToLocalTransform={this.dictationScreenToLocalTransform}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 809a73a77..dce792d19 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -235,7 +235,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
                 PanelWidth={this.documentTitleWidth}
                 PanelHeight={this.documentTitleHeight}
-                scaling={returnOne}
+                NativeDimScaling={returnOne}
                 onKey={this.onKey}
                 docFilters={returnEmptyFilter}
                 docRangeFilters={returnEmptyFilter}
@@ -351,7 +351,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return Doc.NativeHeight(this.Document, undefined, true);
     }
 
-    @computed get contentScaling() {
+    /// scale factor for tree view so that it will fit within it's panel bounds
+    @computed get nativeDimScaling() {
         const nw = this.nativeWidth;
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
@@ -365,7 +366,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins);
     truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
     onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
-    panelWidth = () => Math.max(0, this.props.PanelWidth() - this.marginX() - CollectionTreeView.expandViewLabelSize) * (this.props.scaling?.() || 1);
+    panelWidth = () => Math.max(0, this.props.PanelWidth() - this.marginX() - CollectionTreeView.expandViewLabelSize) * (this.props.NativeDimScaling?.() || 1);
 
     addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
@@ -389,9 +390,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 <div
                     className="collectionTreeView-container"
                     style={{
-                        transform: this.outlineMode ? `scale(${this.contentScaling})` : '',
+                        transform: this.outlineMode ? `scale(${this.nativeDimScaling})` : '',
                         paddingLeft: `${this.marginX()}px`,
-                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : '',
+                        width: this.outlineMode ? `calc(${100 / this.nativeDimScaling}%)` : '',
                     }}
                     onContextMenu={this.onContextMenu}>
                     {!this.buttonMenu && !this.noviceExplainer ? null : (
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 5a2103e98..aa1330762 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -403,7 +403,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const aspect = Doc.NativeAspect(layoutDoc);
         if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]());
         if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth()));
-        return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
+        return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.NativeDimScaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
     };
     docHeight = () => {
         const layoutDoc = this.layoutDoc;
@@ -514,7 +514,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     rtfWidth = () => {
         const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
-        return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1);
+        return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.NativeDimScaling?.() || 1);
     };
     rtfHeight = () => {
         const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
@@ -921,7 +921,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.treeView.props.Document}
-                ContentScaling={returnOne}
             />
         );
 
@@ -992,7 +991,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideResizeHandles={this.props.treeView.outlineMode}
                 onClick={this.onChildClick}
                 focus={this.refocus}
-                ContentScaling={returnOne}
                 onKey={this.onKeyDown}
                 hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
                 dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d7a12122..3e938ec1c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -58,7 +58,7 @@ import e = require('connect-flash');
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
     viewDefDivClick?: ScriptField;
-    childPointerEvents?: boolean;
+    childPointerEvents?: string;
     scaleField?: string;
     noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
     engineProps?: any;
@@ -150,11 +150,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
     }
     @computed get cachedCenteringShiftX(): number {
-        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedCenteringShiftY(): number {
-        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedGetLocalTransform(): Transform {
@@ -1065,7 +1065,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
             // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
@@ -1251,7 +1251,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         if (scale) {
             const maxZoom = 5; // sets the limit for how far we will zoom. this is useful for preventing small text boxes from filling the screen. So probably needs to be more sophisticated to consider more about the target and context
-            const newScale = Math.min(maxZoom, (1 / (this.contentScaling || 1)) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
+            const newScale = Math.min(maxZoom, (1 / (this.nativeDimScaling || 1)) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
             return {
                 panX: this.props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2,
                 panY: this.props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2,
@@ -1301,7 +1301,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     pointerEvents = () => {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
-        const pointerEvents = this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ? 'all' : this.props.viewDefDivClick || (engine === 'pass' && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.();
+        const pointerEvents = this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === 'pass' && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
         return pointerEvents;
     };
     getChildDocView(entry: PoolData) {
@@ -1631,9 +1631,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 img.style.width = canvas.style.width;
                 img.style.height = canvas.style.height;
                 const newCan = newDiv as HTMLCanvasElement;
-                const parEle = newCan.parentElement as HTMLElement;
-                parEle.removeChild(newCan);
-                parEle.appendChild(img);
+                if (newCan) {
+                    const parEle = newCan.parentElement as HTMLElement;
+                    parEle.removeChild(newCan);
+                    parEle.appendChild(img);
+                }
             }
         }
     }
@@ -1943,13 +1945,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         );
     }
 
-    @computed get contentScaling() {
+    @computed get nativeDimScaling() {
         if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0;
         const nw = this.nativeWidth;
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
         const wscale = nw ? this.props.PanelWidth() / nw : 1;
-        return wscale < hscale ? wscale : hscale;
+        return wscale < hscale || this.layoutDoc.fitWidth ? wscale : hscale;
     }
 
     private groupDropDisposer?: DragManager.DragDropDisposer;
@@ -1983,9 +1985,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                             : SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement())
                             ? 'all'
                             : (this.props.pointerEvents?.() as any),
-                    transform: `scale(${this.contentScaling || 1})`,
-                    width: `${100 / (this.contentScaling || 1)}%`,
-                    height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`, // : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
+                    transform: `scale(${this.nativeDimScaling || 1})`,
+                    width: `${100 / (this.nativeDimScaling || 1)}%`,
+                    height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.nativeDimScaling || 1)}%`, // : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
                 }}>
                 {this._firstRender ? this.placeholder : this.marqueeView}
                 {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 777ef464f..465dbfe6d 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,6 +1,6 @@
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
-import * as React from "react";
+import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -11,11 +11,10 @@ import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
-import "./CollectionMulticolumnView.scss";
+import './CollectionMulticolumnView.scss';
 import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
 
-
 interface WidthSpecifier {
     magnitude: number;
     unit: string;
@@ -27,8 +26,8 @@ interface LayoutData {
 }
 
 export const DimUnit = {
-    Pixel: "px",
-    Ratio: "*"
+    Pixel: 'px',
+    Ratio: '*',
 };
 
 const resolvedUnits = Object.values(DimUnit);
@@ -36,14 +35,13 @@ const resizerWidth = 8;
 
 @observer
 export class CollectionMulticolumnView extends CollectionSubView() {
-
     /**
      * @returns the list of layout documents whose width unit is
      * *, denoting that it will be displayed with a ratio, not fixed pixel, value
      */
     @computed
     private get ratioDefinedDocs() {
-        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
+        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, '*') === DimUnit.Ratio);
     }
 
     @computed
@@ -65,10 +63,10 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let starSum = 0;
         const widthSpecifiers: WidthSpecifier[] = [];
         this.childLayoutPairs.map(pair => {
-            const unit = StrCast(pair.layout._dimUnit, "*");
+            const unit = StrCast(pair.layout._dimUnit, '*');
             const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
             if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
-                (unit === DimUnit.Ratio) && (starSum += magnitude);
+                unit === DimUnit.Ratio && (starSum += magnitude);
                 widthSpecifiers.push({ magnitude, unit });
             }
             /**
@@ -100,14 +98,13 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      * This returns the total quantity, in pixels, that this
      * view needs to reserve for child documents that have
      * (with higher priority) requested a fixed pixel width.
-     * 
+     *
      * If the underlying resolvedLayoutInformation returns null
      * because we're waiting on promises to resolve, this value will be undefined as well.
      */
     @computed
     private get totalFixedAllocation(): number | undefined {
-        return this.resolvedLayoutInformation?.widthSpecifiers.reduce(
-            (sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
+        return this.resolvedLayoutInformation?.widthSpecifiers.reduce((sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
     }
 
     /**
@@ -115,7 +112,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      * view needs to reserve for child documents that have
      * (with lower priority) requested a certain relative proportion of the
      * remaining pixel width not allocated for fixed widths.
-     * 
+     *
      * If the underlying totalFixedAllocation returns undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -135,7 +132,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
      * this accessor returns 1000 / (2 + 2 + 1), or 200px.
      * Elsewhere, this is then multiplied by each relative-width
      * document's (potentially decimal) * count to compute its actual width (400px, 400px and 200px).
-     * 
+     *
      * If the underlying totalRatioAllocation or this.resolveLayoutInformation return undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -165,17 +162,17 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             return 0; // we're still waiting on promises to resolve
         }
         let width = NumCast(layout._dimMagnitude, this.minimumDim);
-        if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
+        if (StrCast(layout._dimUnit, '*') === DimUnit.Ratio) {
             width *= columnUnitLength;
         }
         return width;
-    }
+    };
 
     /**
      * @returns the transform that will correctly place
      * the document decorations box, shifted to the right by
      * the sum of all the resolved column widths of the
-     * documents before the target. 
+     * documents before the target.
      */
     private lookupIndividualTransform = (layout: Doc) => {
         const columnUnitLength = this.columnUnitLength;
@@ -185,12 +182,12 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let offset = 0;
         for (const { layout: candidate } of this.childLayoutPairs) {
             if (candidate === layout) {
-                return this.props.ScreenToLocalTransform().translate(-offset / (this.props.scaling?.() || 1), 0);
+                return this.props.ScreenToLocalTransform().translate(-offset / (this.props.NativeDimScaling?.() || 1), 0);
             }
             offset += this.lookupPixels(candidate) + resizerWidth;
         }
         return Transform.Identity(); // type coersion, this case should never be hit
-    }
+    };
 
     @undoBatch
     @action
@@ -198,16 +195,17 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                curInd = this.childDocs.indexOf(d);
-            }));
+            de.complete.docDragData?.droppedDocuments.forEach(
+                action((d: Doc) => {
+                    curInd = this.childDocs.indexOf(d);
+                })
+            );
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.x < de.x && brect.x + brect.width > de.x) {
                     if (curInd !== -1 && curInd === Math.floor(index / 2)) {
                         dropInd = curInd;
-                    }
-                    else if (child.className === "multiColumnResizer") {
+                    } else if (child.className === 'multiColumnResizer') {
                         dropInd = Math.floor(index / 2);
                     } else {
                         dropInd = Math.ceil(index / 2 + (de.x - brect.x > brect.width / 2 ? 0 : -1));
@@ -215,76 +213,80 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 }
             });
             if (super.onInternalDrop(e, de)) {
-                de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                    d._dimUnit = "*";
-                    d._dimMagnitude = 1;
-                    if (dropInd !== curInd || dropInd === -1) {
-                        if (this.childDocs.includes(d)) {
-                            if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                de.complete.docDragData?.droppedDocuments.forEach(
+                    action((d: Doc) => {
+                        d._dimUnit = '*';
+                        d._dimMagnitude = 1;
+                        if (dropInd !== curInd || dropInd === -1) {
+                            if (this.childDocs.includes(d)) {
+                                if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                            }
+                            Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
+                            Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
-                        Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
-                        Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
-                    }
-                }));
+                    })
+                );
             }
         }
         return false;
-    }
-
+    };
 
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     addDocTab = (doc: Doc, where: string) => {
-        if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+        if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
             this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
             return true;
         }
         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;
+    isChildContentActive = () =>
+        ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
-        return <DocumentView
-            Document={layout}
-            DataDoc={layout.resolvedDataDoc as Doc}
-            styleProvider={this.props.styleProvider}
-            docViewPath={this.props.docViewPath}
-            LayoutTemplate={this.props.childLayoutTemplate}
-            LayoutTemplateString={this.props.childLayoutString}
-            renderDepth={this.props.renderDepth + 1}
-            PanelWidth={width}
-            PanelHeight={height}
-            rootSelected={this.rootSelected}
-            dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
-            onClick={this.onChildClickHandler}
-            onDoubleClick={this.onChildDoubleClickHandler}
-            suppressSetHeight={true}
-            ScreenToLocalTransform={dxf}
-            isContentActive={this.isChildContentActive}
-            isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-            hideResizeHandles={this.props.childHideResizeHandles?.()}
-            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToBox={this.props.fitContentsToBox}
-            focus={this.props.focus}
-            docFilters={this.childDocFilters}
-            docRangeFilters={this.childDocRangeFilters}
-            searchFilterDocs={this.searchFilterDocs}
-            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-            ContainingCollectionView={this.props.CollectionView}
-            dontRegisterView={this.props.dontRegisterView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.addDocTab}
-            pinToPres={this.props.pinToPres}
-            bringToFront={returnFalse}
-        />;
-    }
+        return (
+            <DocumentView
+                Document={layout}
+                DataDoc={layout.resolvedDataDoc as Doc}
+                styleProvider={this.props.styleProvider}
+                docViewPath={this.props.docViewPath}
+                LayoutTemplate={this.props.childLayoutTemplate}
+                LayoutTemplateString={this.props.childLayoutString}
+                renderDepth={this.props.renderDepth + 1}
+                PanelWidth={width}
+                PanelHeight={height}
+                rootSelected={this.rootSelected}
+                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                onClick={this.onChildClickHandler}
+                onDoubleClick={this.onChildDoubleClickHandler}
+                suppressSetHeight={true}
+                ScreenToLocalTransform={dxf}
+                isContentActive={this.isChildContentActive}
+                isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+                hideResizeHandles={this.props.childHideResizeHandles?.()}
+                hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+                fitContentsToBox={this.props.fitContentsToBox}
+                focus={this.props.focus}
+                docFilters={this.childDocFilters}
+                docRangeFilters={this.childDocRangeFilters}
+                searchFilterDocs={this.searchFilterDocs}
+                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                ContainingCollectionView={this.props.CollectionView}
+                dontRegisterView={this.props.dontRegisterView}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.addDocTab}
+                pinToPres={this.props.pinToPres}
+                bringToFront={returnFalse}
+            />
+        );
+    };
     /**
      * @returns the resolved list of rendered child documents, displayed
-     * at their resolved pixel widths, each separated by a resizer. 
+     * at their resolved pixel widths, each separated by a resizer.
      */
     @computed
     private get contents(): JSX.Element[] | null {
@@ -293,22 +295,20 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
-            const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)).scale((this.props.scaling?.() || 1));
+            const dxf = () =>
+                this.lookupIndividualTransform(layout)
+                    .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin))
+                    .scale(this.props.NativeDimScaling?.() || 1);
             const width = () => this.lookupPixels(layout);
             const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
             collector.push(
-                <div className={"document-wrapper"}
-                    key={"wrapper" + i}
-                    style={{ width: width() }} >
+                <div className={'document-wrapper'} key={'wrapper' + i} style={{ width: width() }}>
                     {this.getDisplayDoc(layout, dxf, width, height)}
-                    <WidthLabel
-                        layout={layout}
-                        collectionDoc={Document}
-                    />
+                    <WidthLabel layout={layout} collectionDoc={Document} />
                 </div>,
                 <ResizeBar
                     width={resizerWidth}
-                    key={"resizer" + i}
+                    key={'resizer' + i}
                     styleProvider={this.props.styleProvider}
                     isContentActive={this.props.isContentActive}
                     select={this.props.select}
@@ -324,16 +324,19 @@ export class CollectionMulticolumnView extends CollectionSubView() {
 
     render(): JSX.Element {
         return (
-            <div className={"collectionMulticolumnView_contents"} ref={this.createDashEventsTarget}
+            <div
+                className={'collectionMulticolumnView_contents'}
+                ref={this.createDashEventsTarget}
                 style={{
                     width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
                     height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
-                    marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
-                    marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
-                }} >
+                    marginLeft: NumCast(this.props.Document._xMargin),
+                    marginRight: NumCast(this.props.Document._xMargin),
+                    marginTop: NumCast(this.props.Document._yMargin),
+                    marginBottom: NumCast(this.props.Document._yMargin),
+                }}>
                 {this.contents}
             </div>
         );
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 08385bcb5..f8de4e5de 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,6 +1,6 @@
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
-import * as React from "react";
+import * as React from 'react';
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -11,7 +11,7 @@ import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
 import { CollectionSubView } from '../CollectionSubView';
-import "./CollectionMultirowView.scss";
+import './CollectionMultirowView.scss';
 import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
 
@@ -26,8 +26,8 @@ interface LayoutData {
 }
 
 export const DimUnit = {
-    Pixel: "px",
-    Ratio: "*"
+    Pixel: 'px',
+    Ratio: '*',
 };
 
 const resolvedUnits = Object.values(DimUnit);
@@ -35,14 +35,13 @@ const resizerHeight = 8;
 
 @observer
 export class CollectionMultirowView extends CollectionSubView() {
-
     /**
      * @returns the list of layout documents whose width unit is
      * *, denoting that it will be displayed with a ratio, not fixed pixel, value
      */
     @computed
     private get ratioDefinedDocs() {
-        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
+        return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, '*') === DimUnit.Ratio);
     }
 
     @computed
@@ -64,10 +63,10 @@ export class CollectionMultirowView extends CollectionSubView() {
         let starSum = 0;
         const heightSpecifiers: HeightSpecifier[] = [];
         this.childLayoutPairs.map(pair => {
-            const unit = StrCast(pair.layout._dimUnit, "*");
+            const unit = StrCast(pair.layout._dimUnit, '*');
             const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
             if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
-                (unit === DimUnit.Ratio) && (starSum += magnitude);
+                unit === DimUnit.Ratio && (starSum += magnitude);
                 heightSpecifiers.push({ magnitude, unit });
             }
             /**
@@ -99,14 +98,13 @@ export class CollectionMultirowView extends CollectionSubView() {
      * This returns the total quantity, in pixels, that this
      * view needs to reserve for child documents that have
      * (with higher priority) requested a fixed pixel width.
-     * 
+     *
      * If the underlying resolvedLayoutInformation returns null
      * because we're waiting on promises to resolve, this value will be undefined as well.
      */
     @computed
     private get totalFixedAllocation(): number | undefined {
-        return this.resolvedLayoutInformation?.heightSpecifiers.reduce(
-            (sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
+        return this.resolvedLayoutInformation?.heightSpecifiers.reduce((sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
     }
 
     /**
@@ -114,7 +112,7 @@ export class CollectionMultirowView extends CollectionSubView() {
      * view needs to reserve for child documents that have
      * (with lower priority) requested a certain relative proportion of the
      * remaining pixel width not allocated for fixed widths.
-     * 
+     *
      * If the underlying totalFixedAllocation returns undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -134,7 +132,7 @@ export class CollectionMultirowView extends CollectionSubView() {
      * this accessor returns 1000 / (2 + 2 + 1), or 200px.
      * Elsewhere, this is then multiplied by each relative-width
      * document's (potentially decimal) * count to compute its actual width (400px, 400px and 200px).
-     * 
+     *
      * If the underlying totalRatioAllocation or this.resolveLayoutInformation return undefined
      * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
      */
@@ -164,17 +162,17 @@ export class CollectionMultirowView extends CollectionSubView() {
             return 0; // we're still waiting on promises to resolve
         }
         let height = NumCast(layout._dimMagnitude, this.minimumDim);
-        if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
+        if (StrCast(layout._dimUnit, '*') === DimUnit.Ratio) {
             height *= rowUnitLength;
         }
         return height;
-    }
+    };
 
     /**
      * @returns the transform that will correctly place
      * the document decorations box, shifted to the right by
      * the sum of all the resolved row widths of the
-     * documents before the target. 
+     * documents before the target.
      */
     private lookupIndividualTransform = (layout: Doc) => {
         const rowUnitLength = this.rowUnitLength;
@@ -184,13 +182,12 @@ export class CollectionMultirowView extends CollectionSubView() {
         let offset = 0;
         for (const { layout: candidate } of this.childLayoutPairs) {
             if (candidate === layout) {
-                return this.props.ScreenToLocalTransform().translate(0, -offset / (this.props.scaling?.() || 1));
+                return this.props.ScreenToLocalTransform().translate(0, -offset / (this.props.NativeDimScaling?.() || 1));
             }
             offset += this.lookupPixels(candidate) + resizerHeight;
         }
         return Transform.Identity(); // type coersion, this case should never be hit
-    }
-
+    };
 
     @undoBatch
     @action
@@ -198,16 +195,17 @@ export class CollectionMultirowView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                curInd = this.childDocs.indexOf(d);
-            }));
+            de.complete.docDragData?.droppedDocuments.forEach(
+                action((d: Doc) => {
+                    curInd = this.childDocs.indexOf(d);
+                })
+            );
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.y < de.y && brect.y + brect.height > de.y) {
                     if (curInd !== -1 && curInd === Math.floor(index / 2)) {
                         dropInd = curInd;
-                    }
-                    else if (child.className === "multiColumnResizer") {
+                    } else if (child.className === 'multiColumnResizer') {
                         dropInd = Math.floor(index / 2);
                     } else {
                         dropInd = Math.ceil(index / 2 + (de.y - brect.y > brect.height / 2 ? 0 : -1));
@@ -215,75 +213,79 @@ export class CollectionMultirowView extends CollectionSubView() {
                 }
             });
             if (super.onInternalDrop(e, de)) {
-                de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                    d._dimUnit = "*";
-                    d._dimMagnitude = 1;
-                    if (dropInd !== curInd || dropInd === -1) {
-                        if (this.childDocs.includes(d)) {
-                            if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                de.complete.docDragData?.droppedDocuments.forEach(
+                    action((d: Doc) => {
+                        d._dimUnit = '*';
+                        d._dimMagnitude = 1;
+                        if (dropInd !== curInd || dropInd === -1) {
+                            if (this.childDocs.includes(d)) {
+                                if (dropInd > this.childDocs.indexOf(d)) dropInd--;
+                            }
+                            Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
+                            Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
                         }
-                        Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
-                        Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
-                    }
-                }));
+                    })
+                );
             }
         }
         return false;
-    }
-
+    };
 
     onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     addDocTab = (doc: Doc, where: string) => {
-        if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+        if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
             this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
             return true;
         }
         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;
+    isChildContentActive = () =>
+        ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
-        return <DocumentView
-            Document={layout}
-            DataDoc={layout.resolvedDataDoc as Doc}
-            styleProvider={this.props.styleProvider}
-            docViewPath={this.props.docViewPath}
-            LayoutTemplate={this.props.childLayoutTemplate}
-            LayoutTemplateString={this.props.childLayoutString}
-            renderDepth={this.props.renderDepth + 1}
-            PanelWidth={width}
-            PanelHeight={height}
-            rootSelected={this.rootSelected}
-            dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
-            onClick={this.onChildClickHandler}
-            onDoubleClick={this.onChildDoubleClickHandler}
-            ScreenToLocalTransform={dxf}
-            isContentActive={this.isChildContentActive}
-            isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-            hideResizeHandles={this.props.childHideResizeHandles?.()}
-            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToBox={this.props.fitContentsToBox}
-            focus={this.props.focus}
-            docFilters={this.childDocFilters}
-            docRangeFilters={this.childDocRangeFilters}
-            searchFilterDocs={this.searchFilterDocs}
-            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-            ContainingCollectionView={this.props.CollectionView}
-            dontRegisterView={this.props.dontRegisterView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.addDocTab}
-            pinToPres={this.props.pinToPres}
-            bringToFront={returnFalse}
-        />;
-    }
+        return (
+            <DocumentView
+                Document={layout}
+                DataDoc={layout.resolvedDataDoc as Doc}
+                styleProvider={this.props.styleProvider}
+                docViewPath={this.props.docViewPath}
+                LayoutTemplate={this.props.childLayoutTemplate}
+                LayoutTemplateString={this.props.childLayoutString}
+                renderDepth={this.props.renderDepth + 1}
+                PanelWidth={width}
+                PanelHeight={height}
+                rootSelected={this.rootSelected}
+                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                onClick={this.onChildClickHandler}
+                onDoubleClick={this.onChildDoubleClickHandler}
+                ScreenToLocalTransform={dxf}
+                isContentActive={this.isChildContentActive}
+                isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+                hideResizeHandles={this.props.childHideResizeHandles?.()}
+                hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+                fitContentsToBox={this.props.fitContentsToBox}
+                focus={this.props.focus}
+                docFilters={this.childDocFilters}
+                docRangeFilters={this.childDocRangeFilters}
+                searchFilterDocs={this.searchFilterDocs}
+                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                ContainingCollectionView={this.props.CollectionView}
+                dontRegisterView={this.props.dontRegisterView}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.addDocTab}
+                pinToPres={this.props.pinToPres}
+                bringToFront={returnFalse}
+            />
+        );
+    };
     /**
      * @returns the resolved list of rendered child documents, displayed
-     * at their resolved pixel widths, each separated by a resizer. 
+     * at their resolved pixel widths, each separated by a resizer.
      */
     @computed
     private get contents(): JSX.Element[] | null {
@@ -292,13 +294,14 @@ export class CollectionMultirowView extends CollectionSubView() {
         const collector: JSX.Element[] = [];
         for (let i = 0; i < childLayoutPairs.length; i++) {
             const { layout } = childLayoutPairs[i];
-            const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)).scale((this.props.scaling?.() || 1));
+            const dxf = () =>
+                this.lookupIndividualTransform(layout)
+                    .translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin))
+                    .scale(this.props.NativeDimScaling?.() || 1);
             const height = () => this.lookupPixels(layout);
             const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
             collector.push(
-                <div className={"document-wrapper"}
-                    style={{ height: height() }}
-                    key={"wrapper" + i} >
+                <div className={'document-wrapper'} style={{ height: height() }} key={'wrapper' + i}>
                     {this.getDisplayDoc(layout, dxf, width, height)}
                     <HeightLabel layout={layout} collectionDoc={Document} />
                 </div>,
@@ -306,7 +309,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                     height={resizerHeight}
                     styleProvider={this.props.styleProvider}
                     isContentActive={this.props.isContentActive}
-                    key={"resizer" + i}
+                    key={'resizer' + i}
                     columnUnitLength={this.getRowUnitLength}
                     toTop={layout}
                     toBottom={childLayoutPairs[i + 1]?.layout}
@@ -319,16 +322,19 @@ export class CollectionMultirowView extends CollectionSubView() {
 
     render(): JSX.Element {
         return (
-            <div className={"collectionMultirowView_contents"}
+            <div
+                className={'collectionMultirowView_contents'}
                 style={{
                     width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
                     height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
-                    marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
-                    marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
-                }} ref={this.createDashEventsTarget}>
+                    marginLeft: NumCast(this.props.Document._xMargin),
+                    marginRight: NumCast(this.props.Document._xMargin),
+                    marginTop: NumCast(this.props.Document._yMargin),
+                    marginBottom: NumCast(this.props.Document._yMargin),
+                }}
+                ref={this.createDashEventsTarget}>
                 {this.contents}
             </div>
         );
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index f1d8123da..381436a56 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -118,7 +118,7 @@ export class DocumentContentsView extends React.Component<
         FormattedTextBoxProps & {
             isSelected: (outsideReaction: boolean) => boolean;
             select: (ctrl: boolean) => void;
-            scaling?: () => number;
+            NativeDimScaling?: () => number;
             setHeight?: (height: number) => void;
             layoutKey: string;
         }
@@ -161,7 +161,6 @@ export class DocumentContentsView extends React.Component<
             'LayoutTemplateString',
             'LayoutTemplate',
             'dontCenter',
-            'ContentScaling',
             'contextMenuItems',
             'onClick',
             'onDoubleClick',
@@ -195,7 +194,7 @@ export class DocumentContentsView extends React.Component<
 
         // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'>
         const replacer2 = (match: any, p1: string, offset: any, string: any) => {
-            return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.scaling?.() || 1}' htmltag='${p1}'`;
+            return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.NativeDimScaling?.() || 1}' htmltag='${p1}'`;
         };
         layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
 
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 78d35ab99..5939d1680 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -1,24 +1,24 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../fields/Doc";
-import { StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { DragManager } from "../../util/DragManager";
-import { Hypothesis } from "../../util/HypothesisUtils";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { Colors } from "../global/globalEnums";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Opt } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { DragManager } from '../../util/DragManager';
+import { Hypothesis } from '../../util/HypothesisUtils';
+import { LinkManager } from '../../util/LinkManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { Colors } from '../global/globalEnums';
 import './DocumentLinksButton.scss';
-import { DocumentView } from "./DocumentView";
-import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
-import { TaskCompletionBox } from "./TaskCompletedBox";
-import React = require("react");
+import { DocumentView } from './DocumentView';
+import { LinkDescriptionPopup } from './LinkDescriptionPopup';
+import { TaskCompletionBox } from './TaskCompletedBox';
+import React = require('react');
 
-const higflyout = require("@hig/flyout");
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
@@ -28,7 +28,7 @@ interface DocumentLinksButtonProps {
     AlwaysOn?: boolean;
     InMenu?: boolean;
     StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
-    ContentScaling?: () => number;
+    scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
 }
 @observer
 export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -42,53 +42,71 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     @observable public static invisibleWebDoc: Opt<Doc>;
     public static invisibleWebRef = React.createRef<HTMLDivElement>();
 
-    @action @undoBatch
+    @action
+    @undoBatch
     onLinkButtonMoved = (e: PointerEvent) => {
         if (this.props.InMenu && this.props.StartLink) {
             if (this._linkButton.current !== null) {
-                const linkDrag = UndoManager.StartBatch("Drag Link");
-                this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
-                    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");
-                        }
-                        linkDrag?.end();
-                    },
-                    hideSource: false
-                });
+                const linkDrag = UndoManager.StartBatch('Drag Link');
+                this.props.View &&
+                    DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
+                        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');
+                            }
+                            linkDrag?.end();
+                        },
+                        hideSource: false,
+                    });
                 return true;
             }
             return false;
         }
         return false;
-    }
+    };
 
     onLinkMenuOpen = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
-            if (doubleTap) {
-                DocumentView.showBackLinks(this.props.View.rootDoc);
-            }
-        }), undefined, undefined,
-            action(() => DocumentLinksButton.LinkEditorDocView = this.props.View));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            this.onLinkButtonMoved,
+            emptyFunction,
+            action((e, doubleTap) => {
+                if (doubleTap) {
+                    DocumentView.showBackLinks(this.props.View.rootDoc);
+                }
+            }),
+            undefined,
+            undefined,
+            action(() => (DocumentLinksButton.LinkEditorDocView = this.props.View))
+        );
+    };
 
     @undoBatch
     onLinkButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
-            if (doubleTap && this.props.InMenu && this.props.StartLink) {
-                //action(() => Doc.BrushDoc(this.props.View.Document));
-                if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
-                    DocumentLinksButton.StartLink = undefined;
-                    DocumentLinksButton.StartLinkView = undefined;
-                } else {
-                    DocumentLinksButton.StartLink = this.props.View.props.Document;
-                    DocumentLinksButton.StartLinkView = this.props.View;
+        setupMoveUpEvents(
+            this,
+            e,
+            this.onLinkButtonMoved,
+            emptyFunction,
+            action((e, doubleTap) => {
+                if (doubleTap && this.props.InMenu && this.props.StartLink) {
+                    //action(() => Doc.BrushDoc(this.props.View.Document));
+                    if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
+                        DocumentLinksButton.StartLink = undefined;
+                        DocumentLinksButton.StartLinkView = undefined;
+                    } else {
+                        DocumentLinksButton.StartLink = this.props.View.props.Document;
+                        DocumentLinksButton.StartLinkView = this.props.View;
+                    }
                 }
-            }
-        }));
-    }
+            })
+        );
+    };
 
-    @action @undoBatch
+    @action
+    @undoBatch
     onLinkClick = (e: React.MouseEvent): void => {
         if (this.props.InMenu && this.props.StartLink) {
             DocumentLinksButton.AnnotationId = undefined;
@@ -96,108 +114,125 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
-            } else { //if this LinkButton's Document is undefined
+            } else {
+                //if this LinkButton's Document is undefined
                 DocumentLinksButton.StartLink = this.props.View.props.Document;
                 DocumentLinksButton.StartLinkView = this.props.View;
             }
             //action(() => Doc.BrushDoc(this.props.View.Document));
         }
-    }
-
+    };
 
     completeLink = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, 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?.() || 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?
+        setupMoveUpEvents(
+            this,
+            e,
+            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?.() || 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;
+                            LinkManager.currentLink = linkDoc;
 
-                    runInAction(() => {
-                        if (linkDoc) {
-                            TaskCompletionBox.textDisplayed = "Link Created";
-                            TaskCompletionBox.popupX = e.screenX;
-                            TaskCompletionBox.popupY = e.screenY - 133;
-                            TaskCompletionBox.taskCompleted = true;
+                            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;
+                                    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;
-                            }
+                                    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);
+                                    setTimeout(
+                                        action(() => (TaskCompletionBox.taskCompleted = false)),
+                                        2500
+                                    );
+                                }
+                            });
                         }
-                    });
-                }
-            }
-        })));
-    }
+                    }
+                })
+            )
+        );
+    };
 
-    public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
-        if (startLink === endLink) {
-            DocumentLinksButton.StartLink = undefined;
-            DocumentLinksButton.StartLinkView = undefined;
-            DocumentLinksButton.AnnotationId = undefined;
-            DocumentLinksButton.AnnotationUri = undefined;
-            //!this.props.StartLink 
-        } else if (startLink !== endLink) {
-            endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
-            startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
-            const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink },
-                DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : undefined, undefined, undefined, true);
+    public static finishLinkClick = undoBatch(
+        action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView) => {
+            if (startLink === endLink) {
+                DocumentLinksButton.StartLink = undefined;
+                DocumentLinksButton.StartLinkView = undefined;
+                DocumentLinksButton.AnnotationId = undefined;
+                DocumentLinksButton.AnnotationUri = undefined;
+                //!this.props.StartLink
+            } else if (startLink !== endLink) {
+                endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
+                startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
+                const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined, undefined, undefined, true);
 
-            LinkManager.currentLink = linkDoc;
+                LinkManager.currentLink = linkDoc;
 
-            if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) { // if linking from a Hypothes.is annotation
-                Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
-                Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
-                Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
-                const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
-                Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId,
-                    (startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc
-            }
+                if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
+                    // if linking from a Hypothes.is annotation
+                    Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
+                    Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
+                    Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
+                    const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
+                    Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc
+                }
 
-            if (linkDoc) {
-                TaskCompletionBox.textDisplayed = "Link Created";
-                TaskCompletionBox.popupX = screenX;
-                TaskCompletionBox.popupY = screenY - 133;
-                TaskCompletionBox.taskCompleted = true;
+                if (linkDoc) {
+                    TaskCompletionBox.textDisplayed = 'Link Created';
+                    TaskCompletionBox.popupX = screenX;
+                    TaskCompletionBox.popupY = screenY - 133;
+                    TaskCompletionBox.taskCompleted = true;
 
-                if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) {
-                    LinkDescriptionPopup.popupX = screenX;
-                    LinkDescriptionPopup.popupY = screenY - 100;
-                    LinkDescriptionPopup.descriptionPopup = true;
-                }
+                    if (LinkDescriptionPopup.showDescriptions === 'ON' || !LinkDescriptionPopup.showDescriptions) {
+                        LinkDescriptionPopup.popupX = screenX;
+                        LinkDescriptionPopup.popupY = 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;
-                }
+                    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);
+                    setTimeout(
+                        action(() => {
+                            TaskCompletionBox.taskCompleted = false;
+                        }),
+                        2500
+                    );
+                }
             }
-        }
-    }));
+        })
+    );
 
     @action clearLinks() {
         DocumentLinksButton.StartLink = undefined;
@@ -208,9 +243,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.anchor2 as Doc], filters, []).length || DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) {
                 results.push(link);
             }
         });
@@ -219,48 +252,45 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
 
     /**
      * gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node)
-     * 
+     *
      * todo:glr / anh seperate functionality such as onClick onPointerDown of link menu button
      */
     @computed get linkButtonInner() {
-        const btnDim = "30px";
-        const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />;
-        const isActive = (DocumentLinksButton.StartLink === this.props.View.props.Document) && this.props.StartLink;
-        return (!this.props.InMenu ?
-            <div className="documentLinksButton-cont"
-                style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}
-            >
-                <div className={"documentLinksButton"}
-                    onPointerDown={this.onLinkMenuOpen} onClick={this.onLinkClick}
+        const btnDim = '30px';
+        const link = <img style={{ width: '22px', height: '16px' }} src={`/assets/${'link.png'}`} />;
+        const isActive = DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.StartLink;
+        return !this.props.InMenu ? (
+            <div className="documentLinksButton-cont" style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}>
+                <div
+                    className={'documentLinksButton'}
+                    onPointerDown={this.onLinkMenuOpen}
+                    onClick={this.onLinkClick}
                     style={{
                         backgroundColor: Colors.LIGHT_BLUE,
                         color: Colors.BLACK,
-                        fontSize: "20px",
+                        fontSize: '20px',
                         width: btnDim,
                         height: btnDim,
                     }}>
                     {Array.from(this.filteredLinks).length}
                 </div>
             </div>
-            :
-            <div className="documentLinksButton-menu" >
-                {this.props.InMenu && !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}
+        ) : (
+            <div className="documentLinksButton-menu">
+                {this.props.InMenu && !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)}>
                         <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                     </div>
-                    : (null)
-                }
-                {
-                    this.props.InMenu && this.props.StartLink ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
-                        <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton}
-                            onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
-                        </div>
-                        :
-                        (null)
-                }
+                ) : null}
+                {this.props.InMenu && this.props.StartLink ? ( //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
+                    <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton} onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
+                        <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
+                    </div>
+                ) : null}
             </div>
         );
     }
@@ -268,25 +298,23 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     render() {
         TraceMobx();
 
-        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 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.InMenu ? menuTitle : buttonTitle;
 
         //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
-        return (!Array.from(this.filteredLinks).length && !this.props.AlwaysOn) ? (null) :
-            <div className="documentLinksButton-wrapper"
+        return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? null : (
+            <div
+                className="documentLinksButton-wrapper"
                 style={{
-                    transform: this.props.InMenu ? undefined :
-                        `scale(${(this.props.ContentScaling?.() || 1) * this.props.View.screenToLocalTransform().Scale})`
-                }} >
-                {
-                    (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) ||
-                        (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ?
-                        <Tooltip title={<div className="dash-tooltip">{title}</div>}>
-                            {this.linkButtonInner}
-                        </Tooltip>
-                        : this.linkButtonInner
-                }
-            </div>;
+                    transform: this.props.InMenu ? undefined : `scale(${this.props.scaling})`,
+                }}>
+                {(this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) || (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ? (
+                    <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>
+                ) : (
+                    this.linkButtonInner
+                )}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3a8552325..6bf0c365a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -175,7 +175,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     LayoutTemplateString?: string;
     dontCenter?: 'x' | 'y' | 'xy';
     dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
-    ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     LayoutTemplate?: () => Opt<Doc>;
@@ -192,6 +191,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
 export interface DocumentViewInternalProps extends DocumentViewProps {
     NativeWidth: () => number;
     NativeHeight: () => number;
+    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to FieldViewProps
     isSelected: (outsideReaction?: boolean) => boolean;
     select: (ctrlPressed: boolean) => void;
     DocumentView: () => DocumentView;
@@ -235,8 +235,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get ShowTitle() {
         return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
     }
-    @computed get ContentScale() {
-        return this.props.ContentScaling?.() || 1;
+    @computed get NativeDimScaling() {
+        return this.props.NativeDimScaling?.() || 1;
     }
     @computed get thumb() {
         return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
@@ -428,7 +428,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 let nheight = Doc.NativeHeight(layoutDoc);
                 const width = layoutDoc._width || 0;
                 const height = layoutDoc._height || (nheight / nwidth) * width;
-                const scale = this.props.ScreenToLocalTransform().Scale * this.ContentScale;
+                const scale = this.props.ScreenToLocalTransform().Scale * this.NativeDimScaling;
                 const actualdW = Math.max(width + dW * scale, 20);
                 const actualdH = Math.max(height + dH * scale, 20);
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
@@ -491,10 +491,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
         if (this._mainCont.current) {
             const dragData = new DragManager.DocumentDragData([this.props.Document]);
-            const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
+            const [left, top] = this.props.ScreenToLocalTransform().scale(this.NativeDimScaling).inverse().transformPoint(0, 0);
             dragData.offset = this.props
                 .ScreenToLocalTransform()
-                .scale(this.ContentScale)
+                .scale(this.NativeDimScaling)
                 .transformDirection(x - left, y - top);
             dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]);
             dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
@@ -961,7 +961,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
-    contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
     setHeight = (height: number) => (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
@@ -989,6 +988,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             ? true
             : false;
     };
+    linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.props.DocumentView().screenToLocalTransform().Scale;
     @computed get contents() {
         TraceMobx();
         const audioView = !this.layoutDoc._showAudio ? null : (
@@ -1041,7 +1041,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     thumbShown={this.thumbShown}
                     isHovering={this.isHovering}
                     setContentView={this.setContentView}
-                    scaling={this.contentScaling}
+                    NativeDimScaling={this.props.NativeDimScaling}
                     PanelHeight={this.panelHeight}
                     setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
                     isContentActive={this.isContentActive}
@@ -1055,7 +1055,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
                     <DocumentLinksButton
                         View={this.props.DocumentView()}
-                        ContentScaling={this.props.ContentScaling}
+                        scaling={this.linkButtonInverseScaling}
                         Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -30]}
                     />
                 )}
@@ -1503,7 +1503,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
     }
 
-    toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
+    toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
     focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options);
     getBounds = () => {
         if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
@@ -1564,7 +1564,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     NativeHeight = () => this.effectiveNativeHeight;
     PanelWidth = () => this.panelWidth;
     PanelHeight = () => this.panelHeight;
-    ContentScale = () => this.nativeScaling;
+    NativeDimScaling = () => this.nativeScaling;
     selfView = () => this;
     screenToLocalTransform = () =>
         this.props
@@ -1620,9 +1620,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                             PanelHeight={this.PanelHeight}
                             NativeWidth={this.NativeWidth}
                             NativeHeight={this.NativeHeight}
+                            NativeDimScaling={this.NativeDimScaling}
                             isSelected={this.isSelected}
                             select={this.select}
-                            ContentScaling={this.ContentScale}
                             ScreenToLocalTransform={this.screenToLocalTransform}
                             focus={this.props.focus || emptyFunction}
                             ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index c170f9867..a714518cc 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -12,11 +12,12 @@ import { LightboxView } from '../LightboxView';
 import './EquationBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
 
-
 @observer
 export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); }
-    public static SelectOnLoad: string = "";
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(EquationBox, fieldKey);
+    }
+    public static SelectOnLoad: string = '';
     _ref: React.RefObject<EquationEditor> = React.createRef();
     componentDidMount() {
         if (EquationBox.SelectOnLoad === this.rootDoc[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
@@ -25,75 +26,82 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._ref.current!.mathField.focus();
             this._ref.current!.mathField.select();
         }
-        reaction(() => StrCast(this.dataDoc.text),
+        reaction(
+            () => StrCast(this.dataDoc.text),
             text => {
                 if (text && text !== this._ref.current!.mathField.latex()) {
                     this._ref.current!.mathField.latex(text);
                 }
-            });
-        reaction(() => this.props.isSelected(),
+            }
+        );
+        reaction(
+            () => this.props.isSelected(),
             selected => {
                 if (this._ref.current) {
-                    if (selected) this._ref.current.element.current.children[0].addEventListener("keydown", this.keyPressed, true);
-                    else this._ref.current.element.current.children[0].removeEventListener("keydown", this.keyPressed);
+                    if (selected) this._ref.current.element.current.children[0].addEventListener('keydown', this.keyPressed, true);
+                    else this._ref.current.element.current.children[0].removeEventListener('keydown', this.keyPressed);
                 }
-            }, { fireImmediately: true });
+            },
+            { fireImmediately: true }
+        );
     }
     plot: any;
     @action
     keyPressed = (e: KeyboardEvent) => {
-        const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace("px", ""));
-        const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace("px", ""));
-        if (e.key === "Enter") {
+        const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace('px', ''));
+        const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace('px', ''));
+        if (e.key === 'Enter') {
             const nextEq = Docs.Create.EquationDocument({
-                title: "# math", text: StrCast(this.dataDoc.text), _width, _height: 25,
-                x: NumCast(this.layoutDoc.x), y: NumCast(this.layoutDoc.y) + _height + 10
+                title: '# math',
+                text: StrCast(this.dataDoc.text),
+                _width,
+                _height: 25,
+                x: NumCast(this.layoutDoc.x),
+                y: NumCast(this.layoutDoc.y) + _height + 10,
             });
             EquationBox.SelectOnLoad = nextEq[Id];
             this.props.addDocument?.(nextEq);
             e.stopPropagation();
-
         }
-        if (e.key === "Tab") {
+        if (e.key === 'Tab') {
             const graph = Docs.Create.FunctionPlotDocument([this.rootDoc], {
                 x: NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym](),
                 y: NumCast(this.layoutDoc.y),
-                _width: 400, _height: 300, backgroundColor: "white"
+                _width: 400,
+                _height: 300,
+                backgroundColor: 'white',
             });
             this.props.addDocument?.(graph);
             e.stopPropagation();
         }
-        if (e.key === "Backspace" && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
-    }
+        if (e.key === 'Backspace' && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
+    };
     onChange = (str: string) => {
         this.dataDoc.text = str;
         const style = this._ref.current && getComputedStyle(this._ref.current.element.current);
         if (style) {
-            const _height = Number(style.height.replace("px", ""));
-            const _width = Number(style.width.replace("px", ""));
+            const _height = Number(style.height.replace('px', ''));
+            const _width = Number(style.width.replace('px', ''));
             this.layoutDoc._width = Math.max(35, _width);
             this.layoutDoc._height = Math.max(25, _height);
         }
-    }
+    };
     render() {
         TraceMobx();
-        const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
-        return (<div className="equationBox-cont"
-            onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
-            style={{
-                transform: `scale(${scale})`,
-                width: `${100 / scale}%`,
-                height: `${100 / scale}%`,
-                pointerEvents: !this.props.isSelected() ? "none" : undefined,
-            }}
-            onKeyDown={e => e.stopPropagation()}
-        >
-            <EquationEditor ref={this._ref}
-                value={this.dataDoc.text || "x"}
-                spaceBehavesLikeTab={true}
-                onChange={this.onChange}
-                autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
-                autoOperatorNames="sin cos tan" />
-        </div>);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        return (
+            <div
+                className="equationBox-cont"
+                onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
+                style={{
+                    transform: `scale(${scale})`,
+                    width: `${100 / scale}%`,
+                    height: `${100 / scale}%`,
+                    pointerEvents: !this.props.isSelected() ? 'none' : undefined,
+                }}
+                onKeyDown={e => e.stopPropagation()}>
+                <EquationEditor ref={this._ref} value={this.dataDoc.text || 'x'} spaceBehavesLikeTab={true} onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 67cf18d8b..5a6c49809 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,18 +1,18 @@
-import React = require("react");
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { DateField } from "../../../fields/DateField";
-import { Doc, Field, FieldResult, Opt } from "../../../fields/Doc";
-import { List } from "../../../fields/List";
-import { WebField } from "../../../fields/URLField";
-import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
-import { ScriptField } from "../../../fields/ScriptField";
-import { RecordingBox } from "./RecordingBox";
+import React = require('react');
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { DateField } from '../../../fields/DateField';
+import { Doc, Field, FieldResult, Opt } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { WebField } from '../../../fields/URLField';
+import { DocumentView, DocumentViewSharedProps } from './DocumentView';
+import { ScriptField } from '../../../fields/ScriptField';
+import { RecordingBox } from './RecordingBox';
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
 // However, that only happens because the properties are "defined" in the markup for the field view.
-// See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc. 
+// See the LayoutString method on each field view :   ImageBox, FormattedTextBox, etc.
 //
 export interface FieldViewProps extends DocumentViewSharedProps {
     // FieldView specific props that are not part of DocumentView props
@@ -23,10 +23,10 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     isContentActive: (outsideReaction?: boolean) => boolean | undefined;
     isDocumentActive?: () => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
-    scaling?: () => number;
     setHeight?: (height: number) => void;
-    onBrowseClick?: () => (ScriptField | undefined);
-    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
+    NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps
+    onBrowseClick?: () => ScriptField | undefined;
+    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: () => Opt<string>;
@@ -42,7 +42,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldType: { name: string }, fieldStr: string) {
-        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`;  //e.g., "<ImageBox {...props} fieldKey={"data} />"
+        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
     }
 
     @computed
@@ -75,23 +75,22 @@ export class FieldView extends React.Component<FieldViewProps> {
         //}
         else if (field instanceof DateField) {
             return <p>{field.date.toLocaleString()}</p>;
-        }
-        else if (field instanceof Doc) {
-            return <p><b>{field.title?.toString()}</b></p>;
-        }
-        else if (field instanceof List) {
-            return <div> {field.length ? field.map(f => Field.toString(f)).join(", ") : ""}  </div>;
+        } else if (field instanceof Doc) {
+            return (
+                <p>
+                    <b>{field.title?.toString()}</b>
+                </p>
+            );
+        } else if (field instanceof List) {
+            return <div> {field.length ? field.map(f => Field.toString(f)).join(', ') : ''} </div>;
         }
         // bcz: this belongs here, but it doesn't render well so taking it out for now
         else if (field instanceof WebField) {
             return <p>{Field.toString(field.url.href)}</p>;
-        }
-        else if (!(field instanceof Promise)) {
+        } else if (!(field instanceof Promise)) {
             return <p>{Field.toString(field)}</p>;
-        }
-        else {
-            return <p> {"Waiting for server..."} </p>;
+        } else {
+            return <p> {'Waiting for server...'} </p>;
         }
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ffa839fcb..9590bcb15 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -148,8 +148,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
-        cropping._width = anchw * (this.props.scaling?.() || 1);
-        cropping._height = anchh * (this.props.scaling?.() || 1);
+        cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
+        cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
         cropping.isLinkButton = undefined;
         const croppingProto = Doc.GetProto(cropping);
         croppingProto.annotationOn = undefined;
@@ -384,7 +384,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     render() {
         TraceMobx();
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
+        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.NativeDimScaling?.() || 1)}px` : borderRad;
         return (
             <div
                 className="imageBox"
@@ -407,7 +407,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     PanelHeight={this.props.PanelHeight}
                     ScreenToLocalTransform={this.screenToLocalTransform}
                     select={emptyFunction}
-                    scaling={returnOne}
+                    NativeDimScaling={returnOne}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     removeDocument={this.removeDocument}
                     moveDocument={this.moveDocument}
@@ -420,7 +420,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         rootDoc={this.rootDoc}
                         scrollTop={0}
                         down={this._marqueeing}
-                        scaling={this.props.scaling}
+                        scaling={this.props.NativeDimScaling}
                         docView={this.props.docViewPath().slice(-1)[0]}
                         addDocument={this.addDocument}
                         finishMarquee={this.finishMarquee}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c4b42301e..e9ed594d3 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -373,7 +373,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 runInAction(() => {
                     const localDelta = this.props
                         .ScreenToLocalTransform()
-                        .scale(this.props.scaling?.() || 1)
+                        .scale(this.props.NativeDimScaling?.() || 1)
                         .transformDirection(delta[0], delta[1]);
                     const fullWidth = this.layoutDoc[WidthSym]();
                     const mapWidth = fullWidth - this.sidebarWidth();
@@ -559,8 +559,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // }
     };
 
-    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
-    panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
@@ -589,13 +589,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     docFilters={docFilters || this.props.docFilters}
         //     dontRenderDocuments={docFilters ? false : true}
         //     select={emptyFunction}
-        //     ContentScaling={returnOne}
         //     bringToFront={emptyFunction}
         //     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
         //     removeDocument={this.removeDocument}
         //     moveDocument={this.moveDocument}
         //     addDocument={this.sidebarAddDocument}
-        //     childPointerEvents={true}
+        //     childPointerEvents={"all"}
         //     pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="mapBox" ref={this._ref}>
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 72569135b..630ae18f5 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -62,7 +62,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                             setHeight={emptyFunction}
                             isAnnotationOverlay={false}
                             select={emptyFunction}
-                            scaling={returnOne}
+                            NativeDimScaling={returnOne}
                             isContentActive={returnTrue}
                             chromeHidden={true}
                             rootSelected={returnFalse}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index fe18abba9..fad22d6e9 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -104,8 +104,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         const anchx = NumCast(cropping.x);
         const anchy = NumCast(cropping.y);
-        const anchw = cropping[WidthSym]() * (this.props.scaling?.() || 1);
-        const anchh = cropping[HeightSym]() * (this.props.scaling?.() || 1);
+        const anchw = cropping[WidthSym]() * (this.props.NativeDimScaling?.() || 1);
+        const anchh = cropping[HeightSym]() * (this.props.NativeDimScaling?.() || 1);
         const viewScale = 1;
         cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
@@ -290,11 +290,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             (e, down, delta) => {
                 const localDelta = this.props
                     .ScreenToLocalTransform()
-                    .scale(this.props.scaling?.() || 1)
+                    .scale(this.props.NativeDimScaling?.() || 1)
                     .transformDirection(delta[0], delta[1]);
                 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.scaling?.() || 1)) / 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]);
@@ -433,12 +433,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
     }
 
-    contentScaling = () => 1;
     isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected();
     @computed get renderPdfView() {
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const scale = previewScale * (this.props.scaling?.() || 1);
+        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
         return (
             <div
                 className={'pdfBox'}
@@ -473,7 +472,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         removeDocument={this.removeDocument}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                         crop={this.crop}
-                        ContentScaling={returnOne}
                     />
                 </div>
                 <SidebarAnnos
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 942072524..76a24d831 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -295,7 +295,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                             isAnnotationOverlay={true}
                             select={emptyFunction}
                             isContentActive={returnFalse}
-                            scaling={returnOne}
+                            NativeDimScaling={returnOne}
                             whenChildContentsActiveChanged={emptyFunction}
                             removeDocument={returnFalse}
                             moveDocument={returnFalse}
@@ -317,7 +317,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                                 isAnnotationOverlay={true}
                                 select={emptyFunction}
                                 isContentActive={emptyFunction}
-                                scaling={returnOne}
+                                NativeDimScaling={returnOne}
                                 xPadding={25}
                                 yPadding={10}
                                 whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b1f7d8023..681f7c5b2 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -898,7 +898,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
 
-    scaling = () => this.props.scaling?.() || 1;
+    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);
@@ -911,7 +911,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             .scale(100 / this.heightPercent);
     };
 
-    marqueeFitScaling = () => ((this.props.scaling?.() || 1) * this.heightPercent) / 100;
+    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`];
@@ -1124,7 +1124,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                             ScreenToLocalTransform={this.screenToLocalTransform}
                             docFilters={this.timelineDocFilter}
                             select={emptyFunction}
-                            scaling={returnOne}
+                            NativeDimScaling={returnOne}
                             whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                             removeDocument={this.removeDocument}
                             moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 05da8bd7b..71d6959a3 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -200,7 +200,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             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.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -276,7 +276,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
-            const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+            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.getScrollHeight()));
             if (scrollTo !== undefined && this._initialScroll === undefined) {
                 const focusSpeed = smooth ? 500 : 0;
@@ -310,7 +310,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.docViewPath().lastElement()?.docView?.cleanupPointerEvents(); // pointerup events aren't generated on containing document view, so we have to invoke it here.
         if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
             const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
-            const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+            const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
@@ -322,7 +322,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     iframeDown = (e: PointerEvent) => {
         const sel = this._iframe?.contentWindow?.getSelection?.();
         const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
-        const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+        const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
@@ -604,7 +604,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             funcs.push({
                 description: (!this.layoutDoc.forceReflow ? 'Force' : 'Prevent') + ' Reflow',
                 event: () => {
-                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
+                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.NativeDimScaling?.() || 1);
                     this.layoutDoc.forceReflow = !nw;
                     if (nw) {
                         Doc.SetInPlace(this.layoutDoc, this.fieldKey + '-nativeWidth', nw, true);
@@ -717,12 +717,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._draggingSidebar = true;
                 const localDelta = this.props
                     .ScreenToLocalTransform()
-                    .scale(this.props.scaling?.() || 1)
+                    .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 curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / 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);
@@ -834,8 +834,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
-    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
-    panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
@@ -852,7 +852,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     render() {
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
-        const scale = previewScale * (this.props.scaling?.() || 1);
+        const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
         const renderAnnotations = (docFilters?: () => string[]) => (
             <CollectionFreeFormView
                 {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
@@ -864,12 +864,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 PanelWidth={this.panelWidth}
                 PanelHeight={this.panelHeight}
                 ScreenToLocalTransform={this.scrollXf}
-                scaling={returnOne}
+                NativeDimScaling={returnOne}
                 dropAction={'alias'}
                 docFilters={docFilters || this.basicFilter}
                 dontRenderDocuments={docFilters ? false : true}
                 select={emptyFunction}
-                ContentScaling={returnOne}
                 bringToFront={emptyFunction}
                 whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                 removeDocument={this.removeDocument}
@@ -950,7 +949,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
                     setHeight={emptyFunction}
-                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.scaling?.() || 1)}
+                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
                     showSidebar={this.SidebarShown}
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 383c22ba8..e9b048c5e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -968,7 +968,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     @computed get contentScaling() {
-        return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;
+        return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.NativeDimScaling?.() || 1 : 1;
     }
     componentDidMount() {
         !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
@@ -1691,7 +1691,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
     };
     fitContentsToBox = () => this.props.Document._fitContentsToBox;
-    sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+    sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         // console.log("printting allSideBarDocs");
@@ -1705,7 +1705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     sidebarScreenToLocal = () =>
         this.props
             .ScreenToLocalTransform()
-            .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.scaling?.() || 1), 0)
+            .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0)
             .scale(1 / NumCast(this.layoutDoc._viewScale, 1));
 
     @computed get audioHandle() {
@@ -1768,7 +1768,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     scaleField={this.SidebarKey + '-scale'}
                     isAnnotationOverlay={false}
                     select={emptyFunction}
-                    scaling={this.sidebarContentScaling}
+                    NativeDimScaling={this.sidebarContentScaling}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     removeDocument={this.sidebarRemDocument}
                     moveDocument={this.sidebarMoveDocument}
@@ -1793,7 +1793,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         TraceMobx();
         const active = this.props.isContentActive() || this.props.isSelected();
         const selected = active;
-        const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+        const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 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);
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index bd5e8caab..0cf15d297 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -199,11 +199,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    headerUp = (e: React.PointerEvent<HTMLDivElement>) => {
-        e.stopPropagation();
-        e.preventDefault();
-    };
-
     /**
      * Function to drag and drop the pres element to a diferent location
      */
@@ -477,8 +472,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 })}
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
-                onPointerDown={this.headerDown}
-                onPointerUp={this.headerUp}>
+                onPointerDown={this.headerDown}>
                 {/* {miniView ?
                               // when width is LESS than 110 px 
                               <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 837734edf..2c83082b7 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -41,7 +41,6 @@ interface IViewerProps extends FieldViewProps {
     url: string;
     loaded?: (nw: number, nh: number, np: number) => void;
     setPdfViewer: (view: PDFViewer) => void;
-    ContentScaling?: () => number;
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
     crop: (region: Doc | undefined, addCrop?: boolean) => Doc | undefined;
 }
@@ -97,7 +96,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             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.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.NativeDimScaling?.() || 1));
                 }
             }
         );
@@ -162,7 +161,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
-            const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
+            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));
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 focusSpeed = 500;
@@ -486,8 +485,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
     overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
-    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
-    panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
@@ -509,7 +508,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
             PanelWidth={this.panelWidth}
             dropAction={'alias'}
             select={emptyFunction}
-            ContentScaling={this.contentZoom}
             bringToFront={emptyFunction}
             docFilters={docFilters || this.basicFilter}
             styleProvider={this.childStyleProvider}
@@ -556,10 +554,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @computed get pdfViewerDiv() {
         return <div className={'pdfViewerDash-text' + (this.props.pointerEvents?.() !== 'none' && this._textSelecting && this.props.isContentActive() ? '-selected' : '')} ref={this._viewer} />;
     }
-    @computed get contentScaling() {
-        return this.props.ContentScaling?.() || 1;
-    }
-    contentZoom = () => NumCast(this.props.layoutDoc._viewScale, 1);
     savedAnnotations = () => this._savedAnnotations;
     render() {
         TraceMobx();
@@ -574,8 +568,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     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 / this.contentScaling}%`,
-                        transform: `scale(${this.contentScaling})`,
+                        height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `100%`,
                     }}>
                     {this.pdfViewerDiv}
                     {this.annotationLayer}
-- 
cgit v1.2.3-70-g09d2


From e48286293fb37e8af5c448c65c1926e813e8ee1f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 3 Aug 2022 09:27:21 -0400
Subject: from last

---
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 6 +++---
 src/client/views/nodes/trails/PresBox.tsx          | 1 -
 src/client/views/nodes/trails/PresElementBox.tsx   | 1 -
 3 files changed, 3 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 697f8f47f..00bedafbe 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -42,9 +42,9 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
     };
 
     _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
-    childFitWidth = (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);
+    childFitWidth = (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() {
         return (
             <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose}>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index dc01d8305..6e5eb3300 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -144,7 +144,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
     }
     @computed get scrollable(): boolean {
-      //TODO: likely do NOT have to update this for note-taking view, but still worth putting here
         if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
         else return false;
     }
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5ac506f21..0cf15d297 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -202,7 +202,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Function to drag and drop the pres element to a diferent location
      */
-    //TODO: this is what you need to look into
     startDrag = (e: PointerEvent) => {
         const miniView: boolean = this.toolbarWidth <= 100;
         const activeItem = this.rootDoc;
-- 
cgit v1.2.3-70-g09d2


From 5a425e5cf18115921ecb4e7cf931e65f45dab8e2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 18 Aug 2022 11:18:14 -0400
Subject: fixed up presboxelement to not reference presbox.instance.  fixing
 layout of presboxelement in treeview.  fixing "removing" an inkMask without
 removing it from the collection so that it can be turned on and off.

---
 src/client/util/CurrentUserUtils.ts                |   9 +-
 src/client/views/DocumentDecorations.tsx           |   6 +-
 src/client/views/PropertiesView.tsx                |   5 +-
 .../views/collections/CollectionTreeView.scss      |  11 +-
 src/client/views/collections/TabDocView.tsx        |   4 +-
 src/client/views/collections/TreeView.scss         |  17 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   1 +
 src/client/views/nodes/trails/PresBox.tsx          | 137 +++++++++------------
 src/client/views/nodes/trails/PresElementBox.tsx   |  73 ++++++-----
 9 files changed, 132 insertions(+), 131 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index dcf4a71c8..f7d072d80 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -289,11 +289,12 @@ export class CurrentUserUtils {
             { toolTip: "Tap or drag to create a map",             title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, },
             { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreengrab as Doc,  scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'} },
             { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,      scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'}},
-            { toolTip: "Tap or drag to create a button",          title: "Button", icon: "bolt",        dragFactory: doc.emptyButton as Doc,      funcs: { hidden: 'IsNoviceMode()'} },
-            { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a button",          title: "Button", icon: "bolt",        dragFactory: doc.emptyButton as Doc,      funcs:   { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      funcs:   { hidden: 'IsNoviceMode()'}},
             { toolTip: "Tap or drag to create a data viz node",   title: "DataViz", icon: "file",       dragFactory: doc.emptyDataViz as Doc, },
-            { toolTip: "Tap or drag to create a data note",       title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,  scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'},  },
-            { toolTip: "Toggle a Calculator REPL",                title: "repl", icon: "calculator",                                              scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } },
+            { toolTip: "Tap or drag to create a bullet slide",    title: "PPT Slide", icon: "file",     dragFactory: doc.emptySlide as Doc,       funcs:   { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a data note",       title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,scripts: { onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'},  },
+            { toolTip: "Toggle a Calculator REPL",                title: "repl", icon: "calculator",                                              scripts: { onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } },
           ].map(tuple => ({scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, ...tuple, }))
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3544f74b4..6d1397395 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -203,7 +203,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 if (this._deleteAfterIconify) {
                     views.forEach(iconView => {
                         Doc.setNativeView(iconView.props.Document);
-                        iconView.props.removeDocument?.(iconView.props.Document);
+                        if (iconView.props.Document.isInkMask && iconView.props.Document.activeFrame !== undefined) {
+                            iconView.props.Document.opacity = 0; // bcz: hacky ... allows inkMaks to be "turned off" without removing them from the collection which allows them to function properly in a presenation.
+                        } else {
+                            iconView.props.removeDocument?.(iconView.props.Document);
+                        }
                     });
                     SelectionManager.DeselectAll();
                 }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ef0e057dc..33f17047b 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -15,7 +15,7 @@ import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
 import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
-import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
@@ -1614,9 +1614,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             if (this.isPres) {
                 const selectedItem: boolean = PresBox.Instance?._selectedArray.size > 0;
                 const type = PresBox.Instance.activeItem?.type;
-                const viewType = PresBox.Instance.activeItem?._viewType;
-                const pannable: boolean = (type === DocumentType.COL && viewType === CollectionViewType.Freeform) || type === DocumentType.IMG;
-                const scrollable: boolean = type === DocumentType.PDF || type === DocumentType.WEB || type === DocumentType.RTF || viewType === CollectionViewType.Stacking || viewType === CollectionViewType.NoteTaking;
                 return (
                     <div className="propertiesView" style={{ width: this.props.width }}>
                         <div className="propertiesView-title" style={{ width: this.props.width }}>
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 93523a6cf..c0561e42c 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,6 +1,5 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables';
 
-  
 .collectionTreeView-container {
     transform-origin: top left;
     height: 100%;
@@ -28,7 +27,7 @@
         list-style: none;
         padding-left: $TREE_BULLET_WIDTH;
         margin-bottom: 1px; // otherwise vertical scrollbars may pop up for no apparent reason....
-        > .contentFittingDocumentView  {
+        > .contentFittingDocumentView {
             width: unset;
             height: unset;
         }
@@ -39,7 +38,7 @@
 
     .no-indent {
         padding-left: 0;
-        width: max-content;
+        //width: max-content;
     }
 
     .no-indent-outline {
@@ -85,7 +84,7 @@
     width: 100%;
     height: max-content;
     .contentFittingDocumentView {
-        display: block;  // makes titleBar take up full width of the treeView (flex doesn't for some reason)
+        display: block; // makes titleBar take up full width of the treeView (flex doesn't for some reason)
     }
 }
 
@@ -114,4 +113,4 @@
     padding-left: 3px;
     padding-right: 3px;
     padding-bottom: 2px;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index ff4b1e2ce..e147f34d2 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -299,8 +299,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     pinDoc.presMovement = PresMovement.None;
                 }
                 if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-                PresBox.Instance?._selectedArray.clear();
-                pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+                PresBox.Instance?.clearSelectedArray();
+                pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array
             });
             if (
                 CollectionDockingView.Instance &&
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index f587dbbf6..ce87e6f89 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables';
 
 .treeView-label {
     max-height: 1.5em;
@@ -21,7 +21,7 @@
     }
 
     .treeView-bulletIcons {
-        // width: $TREE_BULLET_WIDTH; 
+        // width: $TREE_BULLET_WIDTH;
         width: 100%;
         height: 100%;
 
@@ -101,6 +101,9 @@
 .treeView-border {
     display: flex;
     overflow: hidden;
+    > ul {
+        width: 100%;
+    }
 }
 
 .treeView-border {
@@ -118,7 +121,6 @@
     }
 
     .formattedTextBox-cont {
-
         .formattedTextbox-sidebar,
         .formattedTextbox-sidebar-inking {
             overflow: visible !important;
@@ -144,12 +146,12 @@
         pointer-events: all;
         cursor: pointer;
 
-        >svg {
+        > svg {
             margin-left: 0.25rem;
             margin-right: 0.25rem;
         }
 
-        >svg {
+        > svg {
             //display: none;
             opacity: 0;
             pointer-events: none;
@@ -176,8 +178,7 @@
     }
 
     .treeView-rightButtons {
-
-        >svg,
+        > svg,
         .styleProvider-treeView-icon {
             display: inherit;
             opacity: unset;
@@ -196,4 +197,4 @@
 
 .treeView-header-inside {
     border: black 1px solid;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 45a5e30ff..0fd326091 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -251,6 +251,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
 
     isCurrent(doc: Doc) {
+        if (doc.isInkMask && doc.opacity === 0) return false; // bcz: hacky --- allows inkMasks to be "turned off" in a presentation without removing them from the collection.  otherwise, they still render a gray background.. need to come back to this and fix.
         const dispTime = NumCast(doc._timecodeToShow, -1);
         const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
         const curTime = NumCast(this.Document._currentTimecode, -1);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 627f35e71..05e09361b 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,7 +1,7 @@
 import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
@@ -20,6 +20,7 @@ import { SettingsManager } from '../../../util/SettingsManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
 import { MarqueeViewBounds } from '../../collections/collectionFreeForm';
+import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu';
 import { CollectionView } from '../../collections/CollectionView';
 import { TabDocView } from '../../collections/TabDocView';
 import { ViewBoxBaseComponent } from '../../DocComponent';
@@ -29,7 +30,6 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import './PresBox.scss';
 import { PresEffect, PresMovement, PresStatus } from './PresEnums';
-import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu';
 
 export interface PinProps {
     audioRange?: boolean;
@@ -67,30 +67,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             delay: presDoc.presTransition,
             // when: this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc,
         };
+        //prettier-ignore
         switch (presDoc.presEffect) {
-            case PresEffect.Zoom:
-                return <Zoom {...effectProps}>{renderDoc}</Zoom>;
-            case PresEffect.Fade:
-                return <Fade {...effectProps}>{renderDoc}</Fade>;
-            case PresEffect.Flip:
-                return <Flip {...effectProps}>{renderDoc}</Flip>;
-            case PresEffect.Rotate:
-                return <Rotate {...effectProps}>{renderDoc}</Rotate>;
-            case PresEffect.Bounce:
-                return <Bounce {...effectProps}>{renderDoc}</Bounce>;
-            case PresEffect.Roll:
-                return <Roll {...effectProps}>{renderDoc}</Roll>;
-            case PresEffect.Lightspeed:
-                return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
-            case PresEffect.None:
-            default:
-                return renderDoc;
+            default: 
+            case PresEffect.None:       return renderDoc;
+            case PresEffect.Zoom:       return <Zoom {...effectProps}>{renderDoc}</Zoom>;
+            case PresEffect.Fade:       return <Fade {...effectProps}>{renderDoc}</Fade>;
+            case PresEffect.Flip:       return <Flip {...effectProps}>{renderDoc}</Flip>;
+            case PresEffect.Rotate:     return <Rotate {...effectProps}>{renderDoc}</Rotate>;
+            case PresEffect.Bounce:     return <Bounce {...effectProps}>{renderDoc}</Bounce>;
+            case PresEffect.Roll:       return <Roll {...effectProps}>{renderDoc}</Roll>;
+            case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
         }
     }
     public static EffectsProvider(layoutDoc: Doc, renderDoc: any) {
         return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ? PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex]) : renderDoc;
     }
 
+    private _disposers: { [name: string]: IReactionDisposer } = {};
+
+    constructor(props: any) {
+        super(props);
+        if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
+        this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
+    }
+
     @observable public static Instance: PresBox;
 
     @observable _isChildActive = false;
@@ -98,14 +99,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable _presTimer!: NodeJS.Timeout;
     @observable _presKeyEventsActive: boolean = false;
 
-    @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
     @observable _eleArray: HTMLElement[] = [];
     @observable _dragArray: HTMLElement[] = [];
     @observable _pathBoolean: boolean = false;
     @observable _expandBoolean: boolean = false;
 
-    private _disposers: { [name: string]: IReactionDisposer } = {};
-
     @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView
     @observable private transitionTools: boolean = false;
     @observable private newDocumentTools: boolean = false;
@@ -152,11 +150,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
         else return false;
     }
-    constructor(props: any) {
-        super(props);
-        if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
-        this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
-    }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
         if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
@@ -173,6 +166,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get selectedDoc() {
         return this.selectedDocumentView?.rootDoc;
     }
+    _selectedArray = new ObservableSet<Doc>();
+    clearSelectedArray = () => this._selectedArray.clear();
+    addToSelectedArray = (doc: Doc) => this._selectedArray.add(doc);
+    removeFromSelectedArray = (doc: Doc) => this._selectedArray.delete(doc);
 
     _unmounting = false;
     @action
@@ -188,6 +185,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     componentDidMount() {
+        this.props.setContentView?.(this);
         this._unmounting = false;
         this.rootDoc._forceRenderEngine = 'timeline';
         this.layoutDoc.presStatus = PresStatus.Edit;
@@ -344,8 +342,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (targetDoc?.lastFrame !== undefined) {
                 targetDoc._currentFrame = 0;
             }
-            if (!group) this._selectedArray.clear();
-            this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array
+            if (!group) this.clearSelectedArray();
+            this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array
             this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
             this.onHideDocument(); //Handles hide after/before
         }
@@ -415,7 +413,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.layoutDoc.presCollection = srcContext;
         }
         const presStatus = this.rootDoc.presStatus;
-        const selViewCache = Array.from(this._selectedArray.keys());
+        const selViewCache = Array.from(this._selectedArray);
         const dragViewCache = Array.from(this._dragArray);
         const eleViewCache = Array.from(this._eleArray);
         const self = this;
@@ -423,8 +421,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const presDocView = DocumentManager.Instance.getDocumentView(self.rootDoc);
             if (presDocView) SelectionManager.SelectView(presDocView, false);
             self.rootDoc.presStatus = presStatus;
-            self._selectedArray.clear();
-            selViewCache.forEach(doc => self._selectedArray.set(doc, undefined));
+            self.clearSelectedArray();
+            selViewCache.forEach(doc => self.addToSelectedArray(doc));
             self._dragArray.splice(0, self._dragArray.length, ...dragViewCache);
             self._eleArray.splice(0, self._eleArray.length, ...eleViewCache);
         });
@@ -792,7 +790,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * Method to get the list of selected items in the order in which they have been selected
      */
     @computed get listOfSelected() {
-        const list = Array.from(this._selectedArray.keys()).map((doc: Doc, index: any) => {
+        return Array.from(this._selectedArray).map((doc: Doc, index: any) => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
             if (curDoc && curDoc === this.activeItem)
@@ -816,7 +814,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     </div>
                 );
         });
-        return list;
     }
 
     @action
@@ -838,11 +835,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
         if (!this._selectedArray.has(doc)) {
-            this._selectedArray.set(doc, undefined);
+            this.addToSelectedArray(doc);
             this._eleArray.push(ref);
             this._dragArray.push(drag);
         } else {
-            this._selectedArray.delete(doc);
+            this.removeFromSelectedArray(doc);
             this.removeFromArray(this._eleArray, doc);
             this.removeFromArray(this._dragArray, doc);
         }
@@ -858,11 +855,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //Shift click
     @action
     shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
-        this._selectedArray.clear();
+        this.clearSelectedArray();
         // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
         if (this.activeItem) {
             for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) {
-                this._selectedArray.set(this.childDocs[i], undefined);
+                this.addToSelectedArray(this.childDocs[i]);
                 this._eleArray.push(ref);
                 this._dragArray.push(drag);
             }
@@ -873,8 +870,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //regular click
     @action
     regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
-        this._selectedArray.clear();
-        this._selectedArray.set(doc, undefined);
+        this.clearSelectedArray();
+        this.addToSelectedArray(doc);
         this._eleArray.splice(0, this._eleArray.length, ref);
         this._dragArray.splice(0, this._dragArray.length, drag);
         focus && this.selectElement(doc);
@@ -903,10 +900,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 if (this.layoutDoc.presStatus === 'edit') {
                     undoBatch(
                         action(() => {
-                            for (const doc of Array.from(this._selectedArray.keys())) {
+                            for (const doc of this._selectedArray) {
                                 this.removeDocument(doc);
                             }
-                            this._selectedArray.clear();
+                            this.clearSelectedArray();
                             this._eleArray.length = 0;
                             this._dragArray.length = 0;
                         })
@@ -918,7 +915,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                     this.updateMinimize();
                 } else if (this.layoutDoc.presStatus === 'edit') {
-                    this._selectedArray.clear();
+                    this.clearSelectedArray();
                     this._eleArray.length = this._dragArray.length = 0;
                 } else this.layoutDoc.presStatus = 'edit';
                 if (this._presTimer) clearTimeout(this._presTimer);
@@ -931,7 +928,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) {
                     // TODO: update to work properly
                     this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1;
-                    this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
+                    this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
                 } else {
                     this.next();
                     if (this._presTimer) {
@@ -948,7 +945,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 if (e.shiftKey && this.itemIndex !== 0) {
                     // TODO: update to work properly
                     this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1;
-                    this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
+                    this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]);
                 } else {
                     this.back();
                     if (this._presTimer) {
@@ -966,8 +963,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 break;
             case 'a':
                 if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') {
-                    this._selectedArray.clear();
-                    this.childDocs.forEach(doc => this._selectedArray.set(doc, undefined));
+                    this.clearSelectedArray();
+                    this.childDocs.forEach(doc => this.addToSelectedArray(doc));
                     handled = true;
                 }
             default:
@@ -1115,7 +1112,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 10000) timeInMS = 10000;
-        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presTransition = timeInMS));
+        this._selectedArray.forEach(doc => (doc.presTransition = timeInMS));
     };
 
     // Converts seconds to ms and updates presTransition
@@ -1124,7 +1121,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) scale += change;
         if (scale < 0.01) scale = 0.01;
         if (scale > 1.5) scale = 1.5;
-        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presZoom = scale));
+        this._selectedArray.forEach(doc => (doc.presZoom = scale));
     };
 
     // Converts seconds to ms and updates presDuration
@@ -1133,7 +1130,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 20000) timeInMS = 20000;
-        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presDuration = timeInMS));
+        this._selectedArray.forEach(doc => (doc.presDuration = timeInMS));
     };
 
     /**
@@ -1141,8 +1138,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @undoBatch
     updateMovement = action((movement: any, all?: boolean) => {
-        const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach(doc => {
+        (all ? this.childDocs : this._selectedArray).forEach(doc => {
             switch (movement) {
                 case PresMovement.Zoom: //Pan and zoom
                     doc.presMovement = PresMovement.Zoom;
@@ -1166,21 +1162,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     updateHideBefore = (activeItem: Doc) => {
         activeItem.presHideBefore = !activeItem.presHideBefore;
-        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
+        this._selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
     };
 
     @undoBatch
     @action
     updateHideAfter = (activeItem: Doc) => {
         activeItem.presHideAfter = !activeItem.presHideAfter;
-        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
+        this._selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
     };
 
     @undoBatch
     @action
     updateOpenDoc = (activeItem: Doc) => {
         activeItem.openDocument = !activeItem.openDocument;
-        Array.from(this._selectedArray.keys()).forEach(doc => {
+        this._selectedArray.forEach(doc => {
             doc.openDocument = activeItem.openDocument;
         });
     };
@@ -1188,8 +1184,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     updateEffectDirection = (effect: any, all?: boolean) => {
-        const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach(doc => {
+        (all ? this.childDocs : this._selectedArray).forEach(doc => {
             const tagDoc = doc; // Cast(doc.presentationTargetDoc, Doc, null);
             switch (effect) {
                 case PresEffect.Left:
@@ -1215,29 +1210,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     updateEffect = (effect: any, all?: boolean) => {
-        const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach(doc => {
+        (all ? this.childDocs : this._selectedArray).forEach(doc => {
             const tagDoc = doc; //Cast(doc.presentationTargetDoc, Doc, null);
+            //prettier-ignore
             switch (effect) {
-                case PresEffect.Bounce:
-                    tagDoc.presEffect = PresEffect.Bounce;
-                    break;
-                case PresEffect.Fade:
-                    tagDoc.presEffect = PresEffect.Fade;
-                    break;
-                case PresEffect.Flip:
-                    tagDoc.presEffect = PresEffect.Flip;
-                    break;
-                case PresEffect.Roll:
-                    tagDoc.presEffect = PresEffect.Roll;
-                    break;
-                case PresEffect.Rotate:
-                    tagDoc.presEffect = PresEffect.Rotate;
-                    break;
-                case PresEffect.None:
                 default:
-                    tagDoc.presEffect = PresEffect.None;
-                    break;
+                case PresEffect.None:   tagDoc.presEffect = PresEffect.None; break;
+                case PresEffect.Bounce: tagDoc.presEffect = PresEffect.Bounce; break;
+                case PresEffect.Fade:   tagDoc.presEffect = PresEffect.Fade; break;
+                case PresEffect.Flip:   tagDoc.presEffect = PresEffect.Flip; break;
+                case PresEffect.Roll:   tagDoc.presEffect = PresEffect.Roll; break;
+                case PresEffect.Rotate: tagDoc.presEffect = PresEffect.Rotate; break;
             }
         });
     };
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 0cf15d297..3af8cad9a 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -47,6 +47,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get presStatus() {
         return this.presBox.presStatus;
     }
+    @computed get selectedArray() {
+        return this.presBoxView?._selectedArray;
+    }
+    @computed get presBoxView() {
+        const vpath = this.props.docViewPath();
+        return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as PresBox) : undefined;
+    }
     @computed get presBox() {
         return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!;
     }
@@ -129,10 +136,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             <div
                 className="presItem-groupSlide"
                 onClick={e => {
-                    console.log('Clicked on slide with index: ', ind);
                     e.stopPropagation();
                     e.preventDefault();
-                    PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                    this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
                     this.presExpandDocumentClick();
                 }}>
                 <div className="presItem-groupNum">{`${ind + 1}.`}</div>
@@ -181,15 +187,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         e.stopPropagation();
         e.preventDefault();
         if (element && !(e.ctrlKey || e.metaKey)) {
-            if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
-                PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+            if (this.selectedArray?.has(this.rootDoc)) {
+                this.selectedArray.size === 1 && this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
                 setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
             } else {
                 setupMoveUpEvents(
                     this,
                     e,
                     (e: PointerEvent) => {
-                        PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+                        this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
                         return this.startDrag(e);
                     },
                     emptyFunction,
@@ -205,8 +211,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     startDrag = (e: PointerEvent) => {
         const miniView: boolean = this.toolbarWidth <= 100;
         const activeItem = this.rootDoc;
-        const dragArray = PresBox.Instance._dragArray;
-        const dragData = new DragManager.DocumentDragData(PresBox.Instance.sortArray());
+        const dragArray = this.presBoxView?._dragArray ?? [];
+        const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
         if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
         dragData.dropAction = 'move';
         dragData.treeViewDoc = this.props.docViewPath().lastElement()?.props.treeViewDoc;
@@ -221,7 +227,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         } else if (dragArray.length >= 1) {
             const doc = document.createElement('div');
             doc.className = 'presItem-multiDrag';
-            doc.innerText = 'Move ' + PresBox.Instance._selectedArray.size + ' slides';
+            doc.innerText = 'Move ' + this.selectedArray?.size + ' slides';
             doc.style.position = 'absolute';
             doc.style.top = e.clientY + 'px';
             doc.style.left = e.clientX - 50 + 'px';
@@ -286,10 +292,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     removeItem = action((e: React.MouseEvent) => {
         e.stopPropagation();
-        this.props.removeDocument?.(this.rootDoc);
-        if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
-            PresBox.Instance._selectedArray.delete(this.rootDoc);
+        if (this.indexInPres < (this.presBoxView?.itemIndex || 0)) {
+            this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1;
         }
+        this.props.removeDocument?.(this.rootDoc);
+        this.presBoxView?.removeFromSelectedArray(this.rootDoc);
         this.removeAllRecordingInOverlay();
     });
 
@@ -441,7 +448,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get mainItem() {
-        const isSelected: boolean = PresBox.Instance?._selectedArray.has(this.rootDoc);
+        const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false;
         const toolbarWidth: number = this.toolbarWidth;
         const showMore: boolean = this.toolbarWidth >= 300;
         const miniView: boolean = this.toolbarWidth <= 110;
@@ -463,12 +470,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 onClick={e => {
                     e.stopPropagation();
                     e.preventDefault();
-                    PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                    this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
                     this.showRecording(activeItem);
                 }}
                 onDoubleClick={action(e => {
                     this.toggleProperties();
-                    PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
+                    this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
                 })}
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
@@ -542,21 +549,29 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </Tooltip>
                             )}
 
-                            {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
-                                          <div className="slideButton"
-                                                onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
-                                                style={{
-                                                      zIndex: 1000 - this.indexInPres,
-                                                      fontWeight: 700,
-                                                      backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
-                                                      height: activeItem.groupWithUp ? 53 : 18,
-                                                      transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
-                                                }}>
-                                                <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
-                                                      <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
-                                                </div>
-                                          </div>
-                                    </Tooltip>} */}
+                            {this.indexInPres === 0 ? null : (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        className="slideButton"
+                                        onClick={() => (activeItem.groupWithUp = !activeItem.groupWithUp)}
+                                        style={{
+                                            zIndex: 1000 - this.indexInPres,
+                                            fontWeight: 700,
+                                            backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
+                                            height: activeItem.groupWithUp ? 53 : 18,
+                                            transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
+                                        }}>
+                                        <div style={{ transform: activeItem.groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
+                                            <FontAwesomeIcon icon={'arrow-up'} onPointerDown={e => e.stopPropagation()} />
+                                        </div>
+                                    </div>
+                                </Tooltip>
+                            )}
                             <Tooltip
                                 title={
                                     <>
-- 
cgit v1.2.3-70-g09d2


From 53dc1ae6077774a7235f2fe7f56ffa03f8a9aa5a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 19 Aug 2022 09:21:28 -0400
Subject: fixed undo bug in dragManager where batches weren't being closed. 
 fixed schemaheader copy method to copy all parameters.  fixed notetaking
 columnresizer to create an UndoBatch.  fixed notetakingview's columnHeaders
 to return the actual headers list, not a copy.  fixed document decorations to
 not modify docsBeingDragged

---
 src/client/util/DragManager.ts                     |  2 +-
 src/client/views/DocumentDecorations.tsx           |  4 +-
 .../views/collections/CollectionNoteTakingView.tsx | 20 ++---
 .../CollectionNoteTakingViewDivider.tsx            | 37 +++++----
 .../views/collections/CollectionStackingView.tsx   |  2 -
 src/client/views/nodes/trails/PresElementBox.tsx   |  5 +-
 src/fields/SchemaHeaderField.ts                    | 92 +++++++++++-----------
 7 files changed, 75 insertions(+), 87 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 947882958..eef5b9ce1 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -457,7 +457,7 @@ export namespace DragManager {
             document.removeEventListener('pointerup', upHandler, true);
             SnappingManager.SetIsDragging(false);
             SnappingManager.clearSnapLines();
-            if (undo && batch.end()) UndoManager.Undo();
+            if (batch.end() && undo) UndoManager.Undo();
             docsBeingDragged.length = 0;
         });
         var startWindowDragTimer: any;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0db9eab69..a8dacff51 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -351,8 +351,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        DragManager.docsBeingDragged.push(...SelectionManager.Views().map(dv => dv.rootDoc));
-        this._inkDragDocs = DragManager.docsBeingDragged
+        const views = SelectionManager.Views().map(dv => dv.rootDoc);
+        this._inkDragDocs = views
             .filter(doc => doc.type === DocumentType.INK)
             .map(doc => {
                 if (InkStrokeProperties.Instance._lock) {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 447e1f0c8..0c5f69db0 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -53,7 +53,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
     }
     @computed get columnHeaders() {
-        const columnHeaders = Array.from(Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null));
+        const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
         const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset'));
 
         //  @#Oberable draggedColIndex = ...
@@ -142,17 +142,11 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     @computed get Sections() {
         TraceMobx();
         const columnHeaders = this.columnHeaders;
-        let docs = this.childDocs;
+        // 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, []]));
         const rowCol = this.docsDraggedRowCol;
 
-        // filter out the currently dragged docs from the child docs, since we will insert them later
-        if (rowCol.length && DragManager.docsBeingDragged.length) {
-            const docIdsToRemove = new Set();
-            DragManager.docsBeingDragged.forEach(d => docIdsToRemove.add(d[Id]));
-            docs = docs.filter(d => !docIdsToRemove.has(d[Id]));
-        }
-
         // this will sort the docs into the correct columns (minus the ones you're currently dragging)
         docs.map(d => {
             const sectionValue = (d[this.notetakingCategoryField] as object) ?? `unset`;
@@ -165,11 +159,10 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         });
 
         // now we add back in the docs that we're dragging
-        if (rowCol.length && DragManager.docsBeingDragged.length) {
-            const colHeader = columnHeaders[rowCol[1]];
+        if (rowCol.length) {
             // TODO: get the actual offset that occurs if the docs were in that column
             const offset = 0;
-            sections.get(colHeader)?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged);
+            sections.get(columnHeaders[rowCol[1]])?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged);
         }
         return sections;
     }
@@ -671,7 +664,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
 
     // used to reset column sizes when using the drag handlers
     @action
-    setColumnStartXCoords = (movementX: number, colIndex: number) => {
+    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];
         leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 7d31b3193..2633bffeb 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -1,5 +1,8 @@
 import { action, observable } from 'mobx';
 import * as React from 'react';
+import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
+import { Transform } from '../../util/Transform';
+import { UndoManager } from '../../util/UndoManager';
 
 interface DividerProps {
     index: number;
@@ -13,28 +16,24 @@ export class CollectionNoteTakingViewDivider extends React.Component<DividerProp
 
     @action
     private registerResizing = (e: React.PointerEvent<HTMLDivElement>) => {
-        e.stopPropagation();
-        e.preventDefault();
-        window.removeEventListener('pointermove', this.onPointerMove);
-        window.removeEventListener('pointerup', this.onPointerUp);
-        window.addEventListener('pointermove', this.onPointerMove);
-        window.addEventListener('pointerup', this.onPointerUp);
+        const batch = UndoManager.StartBatch('resizing');
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                this.props.setColumnStartXCoords(delta[0], this.props.index);
+                return false;
+            },
+            action(() => {
+                this.isResizingActive = false;
+                this.isHoverActive = false;
+                batch.end();
+            }),
+            emptyFunction
+        );
         this.isResizingActive = true;
     };
 
-    @action
-    private onPointerUp = () => {
-        this.isResizingActive = false;
-        this.isHoverActive = false;
-        window.removeEventListener('pointermove', this.onPointerMove);
-        window.removeEventListener('pointerup', this.onPointerUp);
-    };
-
-    @action
-    onPointerMove = ({ movementX }: PointerEvent) => {
-        this.props.setColumnStartXCoords(movementX, this.props.index);
-    };
-
     render() {
         return (
             <div
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index ef68cadd7..6683eddf3 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -454,8 +454,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1));
                     docs.splice(insertInd - offset, 0, ...newDocs);
                 }
-                // reset drag manager docs, because we just dropped
-                DragManager.docsBeingDragged.length = 0;
             }
         } 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' });
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 0cf15d297..c578f8bcf 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -250,10 +250,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onPointerMove = (e: PointerEvent) => {
         const slide = this._itemRef.current!;
-        let dragIsPresItem: boolean = DragManager.docsBeingDragged.length > 0 ? true : false;
-        for (const doc of DragManager.docsBeingDragged) {
-            if (!doc.presentationTargetDoc) dragIsPresItem = false;
-        }
+        const dragIsPresItem = DragManager.docsBeingDragged.some(d => d.presentationTargetDoc);
         if (slide && dragIsPresItem) {
             const rect = slide.getBoundingClientRect();
             const y = e.clientY - rect.top; //y position within the element.
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index 3b02d0cfe..1321bc327 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -1,60 +1,60 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, primitive } from "serializr";
-import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString, OnUpdate } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/ScriptingGlobals";
-import { ColumnType } from "../client/views/collections/collectionSchema/CollectionSchemaView";
+import { Deserializable } from '../client/util/SerializationHelper';
+import { serializable, primitive } from 'serializr';
+import { ObjectField } from './ObjectField';
+import { Copy, ToScriptString, ToString, OnUpdate } from './FieldSymbols';
+import { scriptingGlobal } from '../client/util/ScriptingGlobals';
+import { ColumnType } from '../client/views/collections/collectionSchema/CollectionSchemaView';
 
 export const PastelSchemaPalette = new Map<string, string>([
     // ["pink1", "#FFB4E8"],
-    ["pink2", "#ff9cee"],
-    ["pink3", "#ffccf9"],
-    ["pink4", "#fcc2ff"],
-    ["pink5", "#f6a6ff"],
-    ["purple1", "#b28dff"],
-    ["purple2", "#c5a3ff"],
-    ["purple3", "#d5aaff"],
-    ["purple4", "#ecd4ff"],
+    ['pink2', '#ff9cee'],
+    ['pink3', '#ffccf9'],
+    ['pink4', '#fcc2ff'],
+    ['pink5', '#f6a6ff'],
+    ['purple1', '#b28dff'],
+    ['purple2', '#c5a3ff'],
+    ['purple3', '#d5aaff'],
+    ['purple4', '#ecd4ff'],
     // ["purple5", "#fb34ff"],
-    ["purple6", "#dcd3ff"],
-    ["purple7", "#a79aff"],
-    ["purple8", "#b5b9ff"],
-    ["purple9", "#97a2ff"],
-    ["bluegreen1", "#afcbff"],
-    ["bluegreen2", "#aff8db"],
-    ["bluegreen3", "#c4faf8"],
-    ["bluegreen4", "#85e3ff"],
-    ["bluegreen5", "#ace7ff"],
+    ['purple6', '#dcd3ff'],
+    ['purple7', '#a79aff'],
+    ['purple8', '#b5b9ff'],
+    ['purple9', '#97a2ff'],
+    ['bluegreen1', '#afcbff'],
+    ['bluegreen2', '#aff8db'],
+    ['bluegreen3', '#c4faf8'],
+    ['bluegreen4', '#85e3ff'],
+    ['bluegreen5', '#ace7ff'],
     // ["bluegreen6", "#6eb5ff"],
-    ["bluegreen7", "#bffcc6"],
-    ["bluegreen8", "#dbffd6"],
-    ["yellow1", "#f3ffe3"],
-    ["yellow2", "#e7ffac"],
-    ["yellow3", "#ffffd1"],
-    ["yellow4", "#fff5ba"],
+    ['bluegreen7', '#bffcc6'],
+    ['bluegreen8', '#dbffd6'],
+    ['yellow1', '#f3ffe3'],
+    ['yellow2', '#e7ffac'],
+    ['yellow3', '#ffffd1'],
+    ['yellow4', '#fff5ba'],
     // ["red1", "#ffc9de"],
-    ["red2", "#ffabab"],
-    ["red3", "#ffbebc"],
-    ["red4", "#ffcbc1"],
-    ["orange1", "#ffd5b3"],
-    ["gray", "#f1efeb"]
+    ['red2', '#ffabab'],
+    ['red3', '#ffbebc'],
+    ['red4', '#ffcbc1'],
+    ['orange1', '#ffd5b3'],
+    ['gray', '#f1efeb'],
 ]);
 
 export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)];
 
 export const DarkPastelSchemaPalette = new Map<string, string>([
-    ["pink2", "#c932b0"],
-    ["purple4", "#913ad6"],
-    ["bluegreen1", "#3978ed"],
-    ["bluegreen7", "#2adb3e"],
-    ["bluegreen5", "#21b0eb"],
-    ["yellow4", "#edcc0c"],
-    ["red2", "#eb3636"],
-    ["orange1", "#f2740f"],
+    ['pink2', '#c932b0'],
+    ['purple4', '#913ad6'],
+    ['bluegreen1', '#3978ed'],
+    ['bluegreen7', '#2adb3e'],
+    ['bluegreen5', '#21b0eb'],
+    ['yellow4', '#edcc0c'],
+    ['red2', '#eb3636'],
+    ['orange1', '#f2740f'],
 ]);
 
 @scriptingGlobal
-@Deserializable("schemaheader")
+@Deserializable('schemaheader')
 export class SchemaHeaderField extends ObjectField {
     @serializable(primitive())
     heading: string;
@@ -69,7 +69,7 @@ export class SchemaHeaderField extends ObjectField {
     @serializable(primitive())
     desc: boolean | undefined; // boolean determines sort order, undefined when no sort
 
-    constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) {
+    constructor(heading: string = '', color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) {
         super();
 
         this.heading = heading;
@@ -111,7 +111,7 @@ export class SchemaHeaderField extends ObjectField {
     }
 
     [Copy]() {
-        return new SchemaHeaderField(this.heading, this.color, this.type);
+        return new SchemaHeaderField(this.heading, this.color, this.type, this.width, this.desc, this.collapsed);
     }
 
     [ToScriptString]() {
@@ -120,4 +120,4 @@ export class SchemaHeaderField extends ObjectField {
     [ToString]() {
         return `SchemaHeaderField`;
     }
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3-70-g09d2


From 94dbcc40067cb6637f7a535ff305d9452a3f40d1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 24 Aug 2022 14:07:41 -0400
Subject: made text boxes support animation frames. fixed dragging on
 unselected pres element ttitles.  cleaned up setting ink to have animation
 frames.

---
 src/client/documents/Documents.ts                     |  3 ++-
 src/client/views/InkStrokeProperties.ts               | 10 +---------
 src/client/views/InkingStroke.tsx                     | 11 -----------
 .../views/nodes/CollectionFreeFormDocumentView.tsx    | 19 ++++++++++++++++---
 src/client/views/nodes/trails/PresElementBox.tsx      |  8 +++++++-
 src/fields/ScriptField.ts                             | 12 +++++++++++-
 6 files changed, 37 insertions(+), 26 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e579bfd8a..7c50f21a5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1716,7 +1716,8 @@ export namespace DocUtils {
                 .replace(/\.[a-z0-9]*$/, '');
             if (Upload.isImageInformation(result)) {
                 const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
-                proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (StrCast((result.exifData?.data as any)?.Orientation).includes('Rotate 90') ? 5 : undefined);
+                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);
                 if (NumCast(proto['data-nativeOrientation']) >= 5) {
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 1f5f16592..d19a916f9 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -4,7 +4,6 @@ import { Doc, NumListCast, Opt } from '../../fields/Doc';
 import { InkData, InkField, InkTool, PointData } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { listSpec } from '../../fields/Schema';
-import { ComputedField } from '../../fields/ScriptField';
 import { Cast, NumCast } from '../../fields/Types';
 import { Point } from '../../pen-gestures/ndollar';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -12,7 +11,6 @@ import { FitOneCurve } from '../util/bezierFit';
 import { DocumentManager } from '../util/DocumentManager';
 import { undoBatch } from '../util/UndoManager';
 import { InkingStroke } from './InkingStroke';
-import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
 import { DocumentView } from './nodes/DocumentView';
 
 export class InkStrokeProperties {
@@ -67,13 +65,7 @@ export class InkStrokeProperties {
                                 doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth);
                                 doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale;
                                 doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale;
-                                if (doc.activeFrame !== undefined) {
-                                    const findexed = Cast(doc[`data-indexed`], listSpec(InkField), []).slice();
-                                    findexed[NumCast(doc.activeFrame)] = new InkField(newPoints);
-                                    doc[`data-indexed`] = new List<InkField>(findexed);
-                                } else {
-                                    Doc.GetProto(doc).data = new InkField(newPoints);
-                                }
+                                Doc.GetProto(doc).data = new InkField(newPoints);
                                 appliedFunc = true;
                             }
                         }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index ceaabd0e1..dae1c10bb 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -68,17 +68,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     componentDidMount() {
         this.props.setContentView?.(this);
-        this._disposers.activeFrame = reaction(
-            () => this.rootDoc.activeFrame !== undefined && !(ComputedField.WithoutComputed(() => FieldValue(this.rootDoc[this.fieldKey])) instanceof ComputedField),
-            () => {
-                const newPoints = Cast(this.rootDoc[this.fieldKey], InkField, null).inkData;
-                this.rootDoc[this.fieldKey] = ComputedField.MakeInterpolated(this.fieldKey, 'activeFrame', this.rootDoc, NumCast(this.rootDoc.activeFrame));
-                const findexed = Cast(this.rootDoc[`data-indexed`], listSpec(InkField), []).slice();
-                findexed[NumCast(this.rootDoc.activeFrame)] = new InkField(newPoints);
-                this.rootDoc[this.fieldKey + '-indexed'] = new List<InkField>(findexed);
-            },
-            { fireImmediately: true }
-        );
         this._disposers.selfDisper = reaction(
             () => this.props.isSelected(), // react to stroke being deselected by turning off ink handles
             selected => !selected && (InkStrokeProperties.Instance._controlButton = false)
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 45f68e0f0..86566ac6a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -16,6 +16,11 @@ import { StyleProp } from '../StyleProvider';
 import './CollectionFreeFormDocumentView.scss';
 import { DocumentView, DocumentViewProps } from './DocumentView';
 import React = require('react');
+import { InkField } from '../../../fields/InkField';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Field } from '../../util/ProsemirrorCopy/prompt';
+import { RefField } from '../../../fields/RefField';
+import { ObjectField } from '../../../fields/ObjectField';
 
 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;
@@ -33,6 +38,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
     public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // 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 animDataFields = ['data', 'text']; // fields that are configured to be animatable using animation frames
     @observable _animPos: number[] | undefined = undefined;
     @observable _contentView: DocumentView | undefined | null;
     get displayName() {
@@ -118,6 +124,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null);
                     findexed?.length <= timecode + 1 && findexed.push(undefined as any as string);
                 });
+                CollectionFreeFormDocumentView.animDataFields.forEach(val => {
+                    const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null);
+                    findexed?.length <= timecode + 1 && findexed.push(undefined as any);
+                });
             })
         );
         setTimeout(
@@ -164,10 +174,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in
                 doc['opacity-indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1)));
             }
-            CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolated(val, 'activeFrame', doc, currTimecode)));
+            CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedNumber(val, 'activeFrame', doc, currTimecode)));
             CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode)));
-            doc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0');
-            doc.dataTransition = 'inherit';
+            CollectionFreeFormDocumentView.animDataFields.forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode)));
+            const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : 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');
+            targetDoc.dataTransition = 'inherit';
         });
     }
 
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 3af8cad9a..7888d0841 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -501,7 +501,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
                             boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isSelected ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
                         }}>
-                        <div className="presItem-name" style={{ maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
+                        <div
+                            className="presItem-name"
+                            style={{
+                                pointerEvents: isSelected ? undefined : 'none',
+                                maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105,
+                                cursor: isSelected ? 'text' : 'grab',
+                            }}>
                             <div>{`${this.indexInPres + 1}.  `}</div>
                             <EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
                         </div>
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 0fd992d3b..48d5c5563 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -189,7 +189,7 @@ export class ComputedField extends ScriptField {
         const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
         return compiled.compiled ? new ComputedField(compiled) : undefined;
     }
-    public static MakeInterpolated(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
+    public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
         if (!doc[`${fieldKey}-indexed`]) {
             const flist = new List<number>(numberRange(curTimecode + 1).map(i => undefined) as any as number[]);
             flist[curTimecode] = NumCast(doc[fieldKey]);
@@ -209,6 +209,16 @@ export class ComputedField extends ScriptField {
         const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {});
         return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
     }
+    public static MakeInterpolatedDataField(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) {
+        if (!doc[`${fieldKey}-indexed`]) {
+            const flist = new List<Field>(numberRange(curTimecode + 1).map(i => undefined) as any as Field[]);
+            flist[curTimecode] = Field.Copy(doc[fieldKey]);
+            doc[`${fieldKey}-indexed`] = flist;
+        }
+        const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {});
+        const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {});
+        return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
+    }
 }
 export namespace ComputedField {
     let useComputed = true;
-- 
cgit v1.2.3-70-g09d2


From 7278aaa71a13f57cdc371bd771f5fcc6419707b7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 26 Aug 2022 13:25:23 -0400
Subject: added x,y,w,h view pinning for all documents.

---
 src/client/views/DocumentButtonBar.tsx           |  3 +-
 src/client/views/nodes/trails/PresBox.tsx        | 59 +++++++++++++++---------
 src/client/views/nodes/trails/PresElementBox.tsx | 15 +++++-
 3 files changed, 52 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 40fc8dae6..76e2d64a2 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -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;
@@ -243,7 +244,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
                             .views()
                             .filter(v => v)
                             .map(dv => dv!.rootDoc);
-                        TabDocView.PinDoc(docs, { pinDocView: true, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
+                        TabDocView.PinDoc(docs, { pinDocView: !docs.some(doc => !e.shiftKey && doc.type === DocumentType.RTF), activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) });
                     }}>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
                 </div>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index f254eaba6..eb6dad327 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -6,8 +6,10 @@ import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
 import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc';
+import { Copy } from '../../../../fields/FieldSymbols';
 import { InkField, InkTool } from '../../../../fields/InkField';
 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 } from '../../../../Utils';
@@ -29,8 +31,6 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import './PresBox.scss';
 import { PresEffect, PresMovement, PresStatus } from './PresEnums';
-import { Copy } from '../../../../fields/FieldSymbols';
-import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu';
 
 export interface PinProps {
     audioRange?: boolean;
@@ -370,7 +370,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any);
         const clippable = [DocumentType.COMPARISON].includes(target.type as any);
         const dataview = [DocumentType.INK].includes(target.type as any) && target.activeFrame === undefined;
-        return { scrollable, pannable, temporal, clippable, dataview };
+        const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined;
+        return { scrollable, pannable, temporal, clippable, dataview, textview };
     }
     // navigates to the bestTarget document by making sure it is on screen,
     // then it applies the view specs stored in activeItem to
@@ -378,20 +379,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
         const transTime = NumCast(activeItem.presDuration, 500);
         const presTransitionTime = `all ${transTime}ms`;
+        const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(bestTarget);
         bestTarget._viewTransition = presTransitionTime;
-        const { scrollable, pannable, temporal, clippable, dataview } = this.pinDataTypes(bestTarget);
+        bestTarget._dataTransition = dataview || textview ? presTransitionTime : undefined;
+        bestTarget.x = NumCast(activeItem.presX);
+        bestTarget.y = NumCast(activeItem.presY);
+        bestTarget.width = NumCast(activeItem.presWidth);
+        bestTarget.height = NumCast(activeItem.presHeight);
         if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
         if (temporal) bestTarget._currentTimecode = activeItem.presStartTime;
         if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll;
-        if (dataview) {
-            bestTarget._dataTransition = presTransitionTime;
-            bestTarget.data = (activeItem.presData as any as InkField)[Copy]();
-            bestTarget.x = NumCast(activeItem.presX);
-            bestTarget.y = NumCast(activeItem.presY);
-            bestTarget.width = NumCast(activeItem.presWidth);
-            bestTarget.height = NumCast(activeItem.presHeight);
-        }
-
+        if (dataview) Doc.GetProto(bestTarget).data = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+        if (textview) Doc.GetProto(bestTarget).text = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
         if (pannable) {
             const contentBounds = Cast(activeItem.contentBounds, listSpec('number'));
             if (contentBounds) {
@@ -409,7 +408,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         return setTimeout(() => {
             bestTarget._viewTransition = undefined;
-            if (dataview) bestTarget._dataTransition = undefined;
+            if (dataview || textview) bestTarget._dataTransition = undefined;
         }, transTime + 10);
     }
 
@@ -428,19 +427,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             pinDoc.contentBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
         }
         if (pinProps?.pinDocView) {
-            const { scrollable, pannable, temporal, clippable, dataview } = this.pinDataTypes(pinDoc);
-            pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || pinProps.activeFrame !== undefined;
+            const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(pinDoc);
+            pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || textview || pinProps.activeFrame !== undefined;
+            pinDoc.presX = NumCast(targetDoc.x);
+            pinDoc.presY = NumCast(targetDoc.y);
+            pinDoc.presWidth = NumCast(targetDoc.width);
+            pinDoc.presHeight = NumCast(targetDoc.height);
 
             if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop;
             if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth;
             if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1;
-            if (dataview) {
-                pinDoc.presData = (targetDoc.data as InkField)[Copy]();
-                pinDoc.presX = NumCast(targetDoc.x);
-                pinDoc.presY = NumCast(targetDoc.y);
-                pinDoc.presWidth = NumCast(targetDoc.width);
-                pinDoc.presHeight = NumCast(targetDoc.height);
-            }
+            if (textview) pinDoc.presData = targetDoc.text instanceof ObjectField ? targetDoc.text[Copy]() : targetDoc.text;
+            if (dataview) pinDoc.presData = targetDoc.data instanceof ObjectField ? targetDoc.data[Copy]() : targetDoc.data;
             if (pannable) {
                 const panX = NumCast(pinDoc._panX);
                 const panY = NumCast(pinDoc._panY);
@@ -512,9 +510,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // openInTab(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
+            const transTime = NumCast(activeItem.presDuration, 500);
+            const presTransitionTime = `all ${transTime}ms`;
+            targetDoc._dataTransition = presTransitionTime;
+            targetDoc.x = NumCast(activeItem.presX);
+            targetDoc.y = NumCast(activeItem.presY);
+            targetDoc.width = NumCast(activeItem.presWidth);
+            targetDoc.height = NumCast(activeItem.presHeight);
             await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
         } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
+
+            const transTime = NumCast(activeItem.presDuration, 500);
+            const presTransitionTime = `all ${transTime}ms`;
+            targetDoc._dataTransition = presTransitionTime;
+            targetDoc.x = NumCast(activeItem.presX);
+            targetDoc.y = NumCast(activeItem.presY);
+            targetDoc.width = NumCast(activeItem.presWidth);
+            targetDoc.height = NumCast(activeItem.presHeight);
             //awaiting jump so that new scale can be found, since jumping is async
             await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right
         }
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 91196ca21..38a87c34b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
 import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
@@ -24,6 +24,8 @@ import { PresBox } from './PresBox';
 import './PresElementBox.scss';
 import { PresMovement } from './PresEnums';
 import React = require('react');
+import { InkField } from '../../../../fields/InkField';
+import { RichTextField } from '../../../../fields/RichTextField';
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -320,6 +322,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             case DocumentType.RTF:
                 const scroll = targetDoc._scrollTop;
                 activeItem.presPinViewScroll = scroll;
+                if (targetDoc.type === DocumentType.RTF) {
+                    activeItem.presData = targetDoc.text instanceof RichTextField ? targetDoc.text[Copy]() : targetDoc.text;
+                }
+                break;
+            case DocumentType.INK:
+                activeItem.presData = targetDoc.data instanceof InkField ? targetDoc.data[Copy]() : targetDoc.data;
                 break;
             case DocumentType.VID:
             case DocumentType.AUDIO:
@@ -337,6 +345,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 activeItem.presPinViewY = y;
                 activeItem.presPinViewScale = scale;
         }
+
+        activeItem.presX = NumCast(targetDoc.x);
+        activeItem.presY = NumCast(targetDoc.y);
+        activeItem.presWidth = NumCast(targetDoc.width);
+        activeItem.presHeight = NumCast(targetDoc.height);
     };
 
     @computed get recordingIsInOverlay() {
-- 
cgit v1.2.3-70-g09d2


From b1044d1d79c1e06769f74df514e12557426b67be Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 26 Aug 2022 16:16:21 -0400
Subject: trying to clean up transition times for presbox / jumptoDoc, etc.

---
 src/client/util/DocumentManager.ts                 |  18 +-
 .../views/collections/CollectionNoteTakingView.tsx |   2 +-
 .../views/collections/CollectionStackingView.tsx   |   5 +-
 src/client/views/collections/TabDocView.tsx        |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   5 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   1 +
 src/client/views/nodes/trails/PresBox.tsx          | 662 +++++++++------------
 src/client/views/nodes/trails/PresElementBox.tsx   |   9 -
 8 files changed, 287 insertions(+), 417 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 52b643c04..2ca5d1095 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -154,7 +154,7 @@ export class DocumentManager {
         CollectionDockingView.AddSplit(doc, 'right');
         finished?.();
     };
-    public jumpToDocument = async (
+    public jumpToDocument = (
         targetDoc: Doc, // document to display
         willZoom: boolean, // whether to zoom doc to take up most of screen
         createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist
@@ -165,8 +165,8 @@ export class DocumentManager {
         finished?: () => void,
         originalTarget?: Doc,
         noSelect?: boolean,
-        presZoom?: number
-    ): Promise<void> => {
+        presZoomScale?: number
+    ): void => {
         originalTarget = originalTarget ?? targetDoc;
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
         const docView = getFirstDocView(targetDoc, originatingDoc);
@@ -207,7 +207,7 @@ export class DocumentManager {
             finished?.();
         };
         const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc);
-        const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined;
+        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
@@ -218,7 +218,7 @@ export class DocumentManager {
                     annoContainerView.focus(targetDoc, {
                         originalTarget,
                         willZoom,
-                        scale: presZoom,
+                        scale: presZoomScale,
                         afterFocus: (didFocus: boolean) =>
                             new Promise<ViewAdjustment>(res => {
                                 focusAndFinish(true);
@@ -237,7 +237,7 @@ export class DocumentManager {
                 focusView.focus(originalTarget ?? targetDoc, {
                     originalTarget,
                     willZoom,
-                    scale: presZoom,
+                    scale: presZoomScale,
                     afterFocus: (didFocus: boolean) =>
                         new Promise<ViewAdjustment>(res => {
                             focusAndFinish(forceDidFocus || didFocus);
@@ -265,7 +265,9 @@ export class DocumentManager {
                         afterFocus: async () => {
                             targetDocContext._viewTransition = undefined;
                             if (targetDocContext.layoutKey === 'layout_icon') {
-                                targetDocContextView.iconify(() => this.jumpToDocument(resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom));
+                                targetDocContextView.iconify(() =>
+                                    this.jumpToDocument(resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoomScale)
+                                );
                             }
                             return ViewAdjustment.doNothing;
                         },
@@ -309,7 +311,7 @@ export class DocumentManager {
                         const docContextView = this.getFirstDocumentView(docContext[0]);
                         if (docContextView) {
                             return docContextView.iconify(() =>
-                                this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom)
+                                this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoomScale)
                             );
                         }
                     }
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 615141485..92c0bc341 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -201,7 +201,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 = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+                smoothScroll((focusSpeed = NumCast(doc.focusSpeed, 500)), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
             }
         }
         const endFocus = async (moved: boolean) => (options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 71834607c..7f142727c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -259,7 +259,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 = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+                smoothScroll((focusSpeed = NumCast(doc.focusSpeed, 500)), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
             }
         }
         const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
@@ -494,8 +494,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         super.onExternalDrop(e, {}, (docs: Doc[]) => {
             if (targInd === -1) {
                 this.addDocument(docs);
-            }
-            else {
+            } else {
                 const childDocs = this.childDocList;
                 if (childDocs) {
                     childDocs.splice(targInd, 0, ...docs);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f7b48adf6..49228a808 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -366,7 +366,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     focusFunc = (doc: Doc, options?: DocFocusOptions) => {
         const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap;
         if (shrinkwrap && this._document) {
-            const focusSpeed = 1000;
+            const focusSpeed = NumCast(this._document.focusSpeed, 500);
             shrinkwrap();
             this._document._viewTransition = `transform ${focusSpeed}ms`;
             setTimeout(
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 210370d39..ede113a9f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1146,7 +1146,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             }
             // focus on the document in the collection
             const didMove = !cantTransform && !doc.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale);
-            const focusSpeed = options?.instant ? 0 : didMove ? (doc.focusSpeed !== undefined ? Number(doc.focusSpeed) : 500) : 0;
+            const focusSpeed = options?.instant ? 0 : didMove ? NumCast(doc.focusSpeed, 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);
@@ -1452,7 +1452,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     newPos.x !== lastPos.x ||
                     newPos.y !== lastPos.y ||
                     newPos.z !== lastPos.z ||
-                    newPos.zIndex !== lastPos.zIndex
+                    newPos.zIndex !== lastPos.zIndex ||
+                    newPos.transition !== lastPos.transition
                 ) {
                     this._layoutPoolData.set(entry[0], newPos);
                 }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 82557d36b..c07772b53 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -226,6 +226,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
         };
+        console.log('WIdth = ' + this.panelWidth() + ' ' + this.dataProvider?.transition);
         return (
             <div
                 className={'collectionFreeFormDocumentView-container'}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index eb6dad327..ade098917 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -7,7 +7,7 @@ import { ColorState, SketchPicker } from 'react-color';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
 import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc';
 import { Copy } from '../../../../fields/FieldSymbols';
-import { InkField, InkTool } from '../../../../fields/InkField';
+import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
 import { ObjectField } from '../../../../fields/ObjectField';
 import { listSpec } from '../../../../fields/Schema';
@@ -90,7 +90,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     constructor(props: any) {
         super(props);
         if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
-        this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
     }
 
     @observable public static Instance: PresBox;
@@ -127,12 +126,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable _treeViewMap: Map<Doc, number> = new Map();
 
     @computed get tagDocs() {
-        const tagDocs: Doc[] = [];
-        for (const doc of this.childDocs) {
-            const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
-            tagDocs.push(tagDoc);
-        }
-        return tagDocs;
+        return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null));
     }
     @computed get itemIndex() {
         return NumCast(this.rootDoc._itemIndex);
@@ -145,11 +139,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     @computed get scrollable(): boolean {
         if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
-        else return false;
+        return false;
     }
     @computed get panable(): boolean {
         if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
-        else return false;
+        return false;
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
@@ -202,8 +196,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
-        if (pres) Doc.ActivePresentation = pres;
-        else Doc.ActivePresentation = this.rootDoc;
+        Doc.ActivePresentation = pres ?? this.rootDoc;
         document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
@@ -318,7 +311,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const activeItem: Doc = this.activeItem;
             const targetDoc: Doc = this.targetDoc;
             if (activeItem.presActiveFrame !== undefined) {
-                const transTime = NumCast(activeItem.presDuration, 500);
+                const transTime = NumCast(activeItem.presTransition, 500);
                 const context = DocCast(DocCast(activeItem.presentationTargetDoc).context);
                 if (context) {
                     const contextView = DocumentManager.Instance.getFirstDocumentView(context);
@@ -340,12 +333,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             if (targetDoc) {
                 Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc);
-                targetDoc &&
-                    runInAction(() => {
-                        if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
-                        else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
-                    });
-                setTimeout(() => (targetDoc.focusSpeed = 500), this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+                targetDoc && runInAction(() => (targetDoc.focusSpeed = activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500)));
+                setTimeout(() => (targetDoc.focusSpeed = undefined), NumCast(targetDoc.focusSpeed) + 10);
             }
             if (targetDoc?.lastFrame !== undefined) {
                 targetDoc._currentFrame = 0;
@@ -356,14 +345,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.onHideDocument(); //Handles hide after/before
         }
     });
-
-    _navTimer!: NodeJS.Timeout;
-    navigateToView = (targetDoc: Doc, activeItem: Doc) => {
-        clearTimeout(this._navTimer);
-        const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
-        if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
-    };
-
     static pinDataTypes(target: Doc) {
         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);
@@ -373,43 +354,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined;
         return { scrollable, pannable, temporal, clippable, dataview, textview };
     }
-    // navigates to the bestTarget document by making sure it is on screen,
-    // then it applies the view specs stored in activeItem to
+
     @action
-    static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
-        const transTime = NumCast(activeItem.presDuration, 500);
+    static restoreTargetDocView(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
+        const transTime = NumCast(activeItem.presTransition, 500);
         const presTransitionTime = `all ${transTime}ms`;
         const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(bestTarget);
         bestTarget._viewTransition = presTransitionTime;
-        bestTarget._dataTransition = dataview || textview ? presTransitionTime : undefined;
-        bestTarget.x = NumCast(activeItem.presX);
-        bestTarget.y = NumCast(activeItem.presY);
-        bestTarget.width = NumCast(activeItem.presWidth);
-        bestTarget.height = NumCast(activeItem.presHeight);
         if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
         if (temporal) bestTarget._currentTimecode = activeItem.presStartTime;
         if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll;
         if (dataview) Doc.GetProto(bestTarget).data = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
         if (textview) Doc.GetProto(bestTarget).text = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
         if (pannable) {
-            const contentBounds = Cast(activeItem.contentBounds, listSpec('number'));
+            bestTarget._panX = activeItem.presPinViewX;
+            bestTarget._panY = activeItem.presPinViewY;
+            bestTarget._viewScale = activeItem.presPinViewScale;
+            const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number'));
             if (contentBounds) {
-                bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2;
-                bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2;
                 const dv = DocumentManager.Instance.getDocumentView(bestTarget);
-                if (dv) {
-                    bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0]));
-                }
-            } else {
-                bestTarget._panX = activeItem.presPinViewX;
-                bestTarget._panY = activeItem.presPinViewY;
-                bestTarget._viewScale = activeItem.presPinViewScale;
+                dv && (bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0])));
             }
         }
-        return setTimeout(() => {
-            bestTarget._viewTransition = undefined;
-            if (dataview || textview) bestTarget._dataTransition = undefined;
-        }, transTime + 10);
+        return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10);
     }
 
     /// copies values from the targetDoc (which is the prototype of the pinDoc) to
@@ -424,7 +391,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             pinDoc.presPinViewX = bounds.left + bounds.width / 2;
             pinDoc.presPinViewY = bounds.top + bounds.height / 2;
             pinDoc.presPinViewScale = pinProps.pinWithView.scale;
-            pinDoc.contentBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
+            pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
         }
         if (pinProps?.pinDocView) {
             const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(pinDoc);
@@ -446,7 +413,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 const ph = NumCast(pinProps.panelHeight);
                 const ps = NumCast(pinDoc._viewScale);
                 if (pw && ph && ps) {
-                    pinDoc.contentBounds = new List<number>([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]);
+                    pinDoc.presPinViewBounds = new List<number>([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]);
                 }
                 pinDoc.presPinViewX = panX;
                 pinDoc.presPinViewY = panY;
@@ -455,6 +422,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     }
 
+    _navTimer!: NodeJS.Timeout;
     /**
      * This method makes sure that cursor navigates to the element that
      * has the option open and last in the group.
@@ -504,38 +472,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 finished?.();
             }
         };
-        // If openDocument is selected then it should open the document for the user
-        if (activeItem.openDocument) {
-            LightboxView.SetLightboxDoc(targetDoc);
-            // openInTab(targetDoc);
-        } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
-            LightboxView.SetLightboxDoc(undefined);
-            const transTime = NumCast(activeItem.presDuration, 500);
+        if (activeItem.presPinView && DocCast(targetDoc.context)?._currentFrame === undefined) {
+            const transTime = NumCast(activeItem.presTransition, 500);
             const presTransitionTime = `all ${transTime}ms`;
             targetDoc._dataTransition = presTransitionTime;
-            targetDoc.x = NumCast(activeItem.presX);
-            targetDoc.y = NumCast(activeItem.presY);
-            targetDoc.width = NumCast(activeItem.presWidth);
-            targetDoc.height = NumCast(activeItem.presHeight);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
-        } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
+            targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x));
+            targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y));
+            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 && curDoc.presMovement !== PresMovement.None && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-
-            const transTime = NumCast(activeItem.presDuration, 500);
-            const presTransitionTime = `all ${transTime}ms`;
-            targetDoc._dataTransition = presTransitionTime;
-            targetDoc.x = NumCast(activeItem.presX);
-            targetDoc.y = NumCast(activeItem.presY);
-            targetDoc.width = NumCast(activeItem.presWidth);
-            targetDoc.height = NumCast(activeItem.presHeight);
-            //awaiting jump so that new scale can be found, since jumping is async
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right
+            const zooming = curDoc.presMovement !== PresMovement.Pan;
+            DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom));
         }
         // 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.presPinView) {
             // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
-            this.navigateToView(targetDoc, activeItem);
+            clearTimeout(this._navTimer);
+            const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
+            if (bestTarget) this._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem, false);
         }
     };
 
@@ -1488,12 +1449,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'block' }}
                                     className={'toolbar-slider'}
                                     id="duration-slider"
-                                    onPointerDown={() => {
-                                        this._batch = UndoManager.StartBatch('presDuration');
-                                    }}
-                                    onPointerUp={() => {
-                                        if (this._batch) this._batch.end();
-                                    }}
+                                    onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
+                                    onPointerUp={() => this._batch?.end()}
                                     onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                         e.stopPropagation();
                                         this.setDurationTime(e.target.value);
@@ -1650,28 +1607,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
     };
 
-    @computed get mediaStopSlides() {
-        const activeItem: Doc = this.activeItem;
-        const list = this.childDocs.map((doc, i) => {
-            if (i > this.itemIndex) {
-                return (
-                    <option>
-                        {i + 1}. {StrCast(doc.title)}
-                    </option>
-                );
-            }
-        });
-        return list;
-    }
-
     @computed get mediaOptionsDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         const clipStart: number = NumCast(activeItem.clipStart);
         const clipEnd: number = NumCast(activeItem.clipEnd);
-        const duration = Math.round(NumCast(activeItem[`${Doc.LayoutFieldKey(activeItem)}-duration`]) * 10);
         const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc);
-        const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + '. ' + this.childDocs[mediaStopDocInd - 1].title : '';
         if (activeItem && targetDoc) {
             return (
                 <div>
@@ -1842,55 +1783,53 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get newDocumentToolbarDropdown() {
         return (
-            <div>
-                <div
-                    className={'presBox-toolbar-dropdown'}
-                    style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
-                    onClick={e => e.stopPropagation()}
-                    onPointerUp={e => e.stopPropagation()}
-                    onPointerDown={e => e.stopPropagation()}>
-                    <div className="layout-container" style={{ height: 'max-content' }}>
-                        <div
-                            className="layout"
-                            style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
-                            onClick={action(() => {
-                                this.layout = 'blank';
-                                this.createNewSlide(this.layout);
-                            })}
-                        />
-                        <div
-                            className="layout"
-                            style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
-                            onClick={action(() => {
-                                this.layout = 'title';
-                                this.createNewSlide(this.layout);
-                            })}>
-                            <div className="title">Title</div>
-                            <div className="subtitle">Subtitle</div>
-                        </div>
-                        <div
-                            className="layout"
-                            style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
-                            onClick={action(() => {
-                                this.layout = 'header';
-                                this.createNewSlide(this.layout);
-                            })}>
-                            <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
-                                Section header
-                            </div>
+            <div
+                className={'presBox-toolbar-dropdown'}
+                style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
+                onClick={e => e.stopPropagation()}
+                onPointerUp={e => e.stopPropagation()}
+                onPointerDown={e => e.stopPropagation()}>
+                <div className="layout-container" style={{ height: 'max-content' }}>
+                    <div
+                        className="layout"
+                        style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                        onClick={action(() => {
+                            this.layout = 'blank';
+                            this.createNewSlide(this.layout);
+                        })}
+                    />
+                    <div
+                        className="layout"
+                        style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                        onClick={action(() => {
+                            this.layout = 'title';
+                            this.createNewSlide(this.layout);
+                        })}>
+                        <div className="title">Title</div>
+                        <div className="subtitle">Subtitle</div>
+                    </div>
+                    <div
+                        className="layout"
+                        style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                        onClick={action(() => {
+                            this.layout = 'header';
+                            this.createNewSlide(this.layout);
+                        })}>
+                        <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+                            Section header
                         </div>
-                        <div
-                            className="layout"
-                            style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
-                            onClick={action(() => {
-                                this.layout = 'content';
-                                this.createNewSlide(this.layout);
-                            })}>
-                            <div className="title" style={{ alignSelf: 'center' }}>
-                                Title
-                            </div>
-                            <div className="content">Text goes here</div>
+                    </div>
+                    <div
+                        className="layout"
+                        style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                        onClick={action(() => {
+                            this.layout = 'content';
+                            this.createNewSlide(this.layout);
+                        })}>
+                        <div className="title" style={{ alignSelf: 'center' }}>
+                            Title
                         </div>
+                        <div className="content">Text goes here</div>
                     </div>
                 </div>
             </div>
@@ -1904,73 +1843,71 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get newDocumentDropdown() {
         return (
-            <div>
-                <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
-                    <div className="ribbon-box">
-                        Slide Title: <br></br>
-                        <input
-                            className="ribbon-textInput"
-                            placeholder="..."
-                            type="text"
-                            name="fname"
-                            onChange={e => {
-                                e.stopPropagation();
-                                e.preventDefault();
-                                runInAction(() => (this.title = e.target.value));
-                            }}></input>
+            <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                <div className="ribbon-box">
+                    Slide Title: <br></br>
+                    <input
+                        className="ribbon-textInput"
+                        placeholder="..."
+                        type="text"
+                        name="fname"
+                        onChange={e => {
+                            e.stopPropagation();
+                            e.preventDefault();
+                            runInAction(() => (this.title = e.target.value));
+                        }}></input>
+                </div>
+                <div className="ribbon-box">
+                    Choose type:
+                    <div className="ribbon-doubleButton">
+                        <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? '' : Colors.LIGHT_BLUE }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+                            Text
+                        </div>
+                        <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : '' }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+                            Freeform
+                        </div>
                     </div>
-                    <div className="ribbon-box">
-                        Choose type:
-                        <div className="ribbon-doubleButton">
-                            <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? '' : Colors.LIGHT_BLUE }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
-                                Text
-                            </div>
-                            <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : '' }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
-                                Freeform
+                </div>
+                <div className="ribbon-box" style={{ display: this.addFreeform ? 'grid' : 'none' }}>
+                    Preset layouts:
+                    <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
+                        <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'blank'))} />
+                        <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'title'))}>
+                            <div className="title">Title</div>
+                            <div className="subtitle">Subtitle</div>
+                        </div>
+                        <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'header'))}>
+                            <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+                                Section header
                             </div>
                         </div>
-                    </div>
-                    <div className="ribbon-box" style={{ display: this.addFreeform ? 'grid' : 'none' }}>
-                        Preset layouts:
-                        <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
-                            <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'blank'))} />
-                            <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'title'))}>
-                                <div className="title">Title</div>
-                                <div className="subtitle">Subtitle</div>
+                        <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'content'))}>
+                            <div className="title" style={{ alignSelf: 'center' }}>
+                                Title
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'header'))}>
-                                <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
-                                    Section header
-                                </div>
+                            <div className="content">Text goes here</div>
+                        </div>
+                        <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'twoColumns'))}>
+                            <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>
+                                Title
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'content'))}>
-                                <div className="title" style={{ alignSelf: 'center' }}>
-                                    Title
-                                </div>
-                                <div className="content">Text goes here</div>
+                            <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>
+                                Column one text
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'twoColumns'))}>
-                                <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>
-                                    Title
-                                </div>
-                                <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>
-                                    Column one text
-                                </div>
-                                <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>
-                                    Column two text
-                                </div>
+                            <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>
+                                Column two text
                             </div>
                         </div>
-                        <div className="open-layout" onClick={action(() => (this.openLayouts = !this.openLayouts))}>
-                            <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'caret-down'} size={'lg'} />
-                        </div>
                     </div>
-                    <div className="ribbon-final-box">
-                        <div
-                            className={this.title !== '' && ((this.addFreeform && this.layout !== '') || !this.addFreeform) ? 'ribbon-final-button-hidden' : 'ribbon-final-button'}
-                            onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
-                            Create New Slide
-                        </div>
+                    <div className="open-layout" onClick={action(() => (this.openLayouts = !this.openLayouts))}>
+                        <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'caret-down'} size={'lg'} />
+                    </div>
+                </div>
+                <div className="ribbon-final-box">
+                    <div
+                        className={this.title !== '' && ((this.addFreeform && this.layout !== '') || !this.addFreeform) ? 'ribbon-final-button-hidden' : 'ribbon-final-button'}
+                        onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
+                        Create New Slide
                     </div>
                 </div>
             </div>
@@ -2105,115 +2042,109 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             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>
-                    <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 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>
-                            {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 className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editProgressivize}>
+                                Edit
                             </div>
-                            {this.viewedColorPicker}
+                        </div>
+                        <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
+                            <div className="presBox-subheading">Active text color</div>
                             <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>
+                                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-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 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-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
+                            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-frameList">
-                                {this.frameListHeader}
-                                {this.frameList}
+                            <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editScrollProgressivize}>
+                                Edit
                             </div>
-                            <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(' TODO: play frames')}>
-                                Play
+                        </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>
@@ -2233,7 +2164,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     switchPresented = (color: ColorState) => {
-        const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         const val = String(color.hex);
         targetDoc['pres-text-viewed-color'] = val;
@@ -2241,25 +2171,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     @computed get activeColorPicker() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
         return !this.openActiveColorPicker ? null : (
             <SketchPicker
                 onChange={this.switchActive}
                 presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
-                color={StrCast(targetDoc['pres-text-color'])}
+                color={StrCast(this.targetDoc['pres-text-color'])}
             />
         );
     }
 
     @computed get viewedColorPicker() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
         return !this.openViewedColorPicker ? null : (
             <SketchPicker
                 onChange={this.switchPresented}
                 presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
-                color={StrCast(targetDoc['pres-text-viewed-color'])}
+                color={StrCast(this.targetDoc['pres-text-viewed-color'])}
             />
         );
     }
@@ -2422,10 +2348,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     @observable
-    toggleDisplayMovement = (doc: Doc) => {
-        if (doc.displayMovement) doc.displayMovement = false;
-        else doc.displayMovement = true;
-    };
+    toggleDisplayMovement = (doc: Doc) => (doc.displayMovement = !doc.displayMovement);
 
     @action
     checkList = (doc: Doc, list: any): number => {
@@ -2538,24 +2461,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    @computed get moreInfoDropdown() {
-        return <div></div>;
-    }
-
     @computed
     get toolbarWidth(): number {
-        const width = this.props.PanelWidth();
-        return width;
+        return this.props.PanelWidth();
     }
 
     @action
-    toggleProperties = () => {
-        if (SettingsManager.propertiesWidth > 0) {
-            SettingsManager.propertiesWidth = 0;
-        } else {
-            SettingsManager.propertiesWidth = 250;
-        }
-    };
+    toggleProperties = () => (SettingsManager.propertiesWidth = SettingsManager.propertiesWidth > 0 ? 0 : 250);
 
     @computed get toolbar() {
         const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
@@ -2677,10 +2589,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @action
-    getList = (list: any): List<number> => {
-        const x: List<number> = list;
-        return x;
-    };
+    getList = (list: any): List<number> => list;
 
     @action
     updateList = (list: any): List<number> => {
@@ -2709,12 +2618,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             <div className="frameList-header">
                 &nbsp; Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : null}
                 <div className={'frameList-headerButtons'}>
-                    <Tooltip
-                        title={
-                            <>
-                                <div className="dash-tooltip">{'Add frame by example'}</div>
-                            </>
-                        }>
+                    <Tooltip title={<div className="dash-tooltip">{'Add frame by example'}</div>}>
                         <div
                             className={'headerButton'}
                             onClick={e => {
@@ -2724,12 +2628,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             <FontAwesomeIcon icon={'plus'} onPointerDown={e => e.stopPropagation()} />
                         </div>
                     </Tooltip>
-                    <Tooltip
-                        title={
-                            <>
-                                <div className="dash-tooltip">{'Edit in collection'}</div>
-                            </>
-                        }>
+                    <Tooltip title={<div className="dash-tooltip">{'Edit in collection'}</div>}>
                         <div
                             className={'headerButton'}
                             onClick={e => {
@@ -2746,7 +2645,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get frameList() {
         const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
         const frameList: List<number> = this.getList(activeItem.frameList);
         if (frameList) {
             const frameItems = frameList.map(value => <div className="framList-item"></div>);
@@ -2755,18 +2653,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get playButtonFrames() {
-        const targetDoc: Doc = this.targetDoc;
-        return (
-            <>
-                {this.targetDoc ? (
-                    <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}>
-                        <div>{NumCast(targetDoc._currentFrame)}</div>
-                        <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
-                        <div>{NumCast(targetDoc.lastFrame)}</div>
-                    </div>
-                ) : null}
-            </>
-        );
+        const targetDoc = this.targetDoc;
+        return this.targetDoc ? (
+            <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}>
+                <div>{NumCast(targetDoc._currentFrame)}</div>
+                <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
+                <div>{NumCast(targetDoc.lastFrame)}</div>
+            </div>
+        ) : null;
     }
 
     @computed get playButtons() {
@@ -2775,12 +2669,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // Case 1: There are still other frames and should go through all frames before going to next slide
         return (
             <div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
-                <Tooltip
-                    title={
-                        <>
-                            <div className="dash-tooltip">{'Loop'}</div>
-                        </>
-                    }>
+                <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
                     <div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}>
                         <FontAwesomeIcon icon={'redo-alt'} />
                     </div>
@@ -2798,12 +2687,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     }}>
                     <FontAwesomeIcon icon={'arrow-left'} />
                 </div>
-                <Tooltip
-                    title={
-                        <>
-                            <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
-                        </>
-                    }>
+                <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                     <div className="presPanel-button" onClick={this.startOrPause}>
                         <FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
                     </div>
@@ -2821,12 +2705,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <FontAwesomeIcon icon={'arrow-right'} />
                 </div>
                 <div className="presPanel-divider"></div>
-                <Tooltip
-                    title={
-                        <>
-                            <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
-                        </>
-                    }>
+                <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}>
                     <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0, this.activeItem)}>
                         <b>1</b>
                     </div>
@@ -2931,12 +2810,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc) ? (
             <div className="miniPres" onClick={e => e.stopPropagation()}>
                 <div className="presPanelOverlay" style={{ display: 'inline-flex', height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
-                    <Tooltip
-                        title={
-                            <>
-                                <div className="dash-tooltip">{'Loop'}</div>
-                            </>
-                        }>
+                    <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}>
                         <div
                             className="presPanel-button"
                             style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
@@ -2948,12 +2822,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
                         <FontAwesomeIcon icon={'arrow-left'} />
                     </div>
-                    <Tooltip
-                        title={
-                            <>
-                                <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
-                            </>
-                        }>
+                    <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}>
                         <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}>
                             <FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} />
                         </div>
@@ -2962,12 +2831,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <FontAwesomeIcon icon={'arrow-right'} />
                     </div>
                     <div className="presPanel-divider"></div>
-                    <Tooltip
-                        title={
-                            <>
-                                <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
-                            </>
-                        }>
+                    <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}>
                         <div className="presPanel-button" style={{ border: 'solid 1px white' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}>
                             <b>1</b>
                         </div>
@@ -3033,5 +2897,17 @@ ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) {
         CollectionDockingView.AddSplit(doc, 'right');
         finished?.();
     };
-    DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
+    DocumentManager.Instance.jumpToDocument(
+        bestTarget,
+        true,
+        openInTab,
+        srcContext ? [srcContext] : [],
+        undefined,
+        undefined,
+        undefined,
+        () => PresBox.restoreTargetDocView(bestTarget, activeItem, true),
+        undefined,
+        true,
+        NumCast(activeItem.presZoom)
+    );
 });
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 38a87c34b..c78828a78 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -162,15 +162,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         ));
         return groupSlides;
     }
-    @computed get duration() {
-        let durationInS: number;
-        if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) {
-            durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime);
-            durationInS = Math.round(durationInS * 10) / 10;
-        } else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
-        else durationInS = 2;
-        return 'D: ' + durationInS + 's';
-    }
 
     @computed get transition() {
         let transitionInS: number;
-- 
cgit v1.2.3-70-g09d2


From a4ce2913b8a15cdd4670002a4a74f1d86601348e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 26 Aug 2022 18:34:41 -0400
Subject: a bunch of mostly decorative cleanup to presBox

---
 src/client/views/PropertiesView.tsx              |   6 +-
 src/client/views/collections/TabDocView.tsx      |   2 +-
 src/client/views/nodes/trails/PresBox.tsx        | 558 +++++++----------------
 src/client/views/nodes/trails/PresElementBox.tsx |   2 +-
 src/client/views/nodes/trails/PresEnums.ts       |  42 +-
 5 files changed, 186 insertions(+), 424 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 33f17047b..2708c561d 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -58,7 +58,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
-        if (PresBox.Instance?._selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
+        if (PresBox.Instance?.selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
         return undefined;
     }
     @computed get isPres(): boolean {
@@ -1612,7 +1612,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 );
             }
             if (this.isPres) {
-                const selectedItem: boolean = PresBox.Instance?._selectedArray.size > 0;
+                const selectedItem: boolean = PresBox.Instance?.selectedArray.size > 0;
                 const type = PresBox.Instance.activeItem?.type;
                 return (
                     <div className="propertiesView" style={{ width: this.props.width }}>
@@ -1622,7 +1622,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <div className="propertiesView-name" style={{ borderBottom: 0 }}>
                             {this.editableTitle}
                             <div className="propertiesView-presSelected">
-                                <div className="propertiesView-selectedCount">{PresBox.Instance?._selectedArray.size} selected</div>
+                                <div className="propertiesView-selectedCount">{PresBox.Instance?.selectedArray.size} selected</div>
                                 <div className="propertiesView-selectedList">{PresBox.Instance?.listOfSelected}</div>
                             </div>
                         </div>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 49228a808..bead5825c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -239,7 +239,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.treeViewGrowsHorizontally = true; // the document expands horizontally when displayed as a tree view header
                 pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
                 const presArray: Doc[] = PresBox.Instance?.sortArray();
-                const size: number = PresBox.Instance?._selectedArray.size;
+                const size: number = PresBox.Instance?.selectedArray.size;
                 const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
                 const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index ade098917..ac68ea281 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -12,7 +12,7 @@ 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 } from '../../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents, StopEvent } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
@@ -86,6 +86,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     private _disposers: { [name: string]: IReactionDisposer } = {};
+    public selectedArray = new ObservableSet<Doc>();
 
     constructor(props: any) {
         super(props);
@@ -93,6 +94,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @observable public static Instance: PresBox;
+    @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView
 
     @observable _isChildActive = false;
     @observable _moveOnFromAudio: boolean = true;
@@ -103,14 +105,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable _dragArray: HTMLElement[] = [];
     @observable _pathBoolean: boolean = false;
     @observable _expandBoolean: boolean = false;
-
-    @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView
-    @observable private transitionTools: boolean = false;
-    @observable private newDocumentTools: boolean = false;
-    @observable private progressivizeTools: boolean = false;
-    @observable private openMovementDropdown: boolean = false;
-    @observable private openEffectDropdown: boolean = false;
-    @observable private presentTools: 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;
+    @observable _treeViewMap: Map<Doc, number> = new Map();
     @computed get isTreeOrStack() {
         return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any);
     }
@@ -123,8 +124,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get childDocs() {
         return DocListCast(this.rootDoc[this.presFieldKey]);
     }
-    @observable _treeViewMap: Map<Doc, number> = new Map();
-
     @computed get tagDocs() {
         return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null));
     }
@@ -137,22 +136,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get targetDoc() {
         return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
     }
-    @computed get scrollable(): boolean {
+    @computed get scrollable() {
         if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
         return false;
     }
-    @computed get panable(): boolean {
+    @computed get panable() {
         if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
         return false;
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
-        if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
+        if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
     }
-    @computed get isPres(): boolean {
+    @computed get isPres() {
         document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         if (this.selectedDoc?.type === DocumentType.PRES) {
-            document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
             document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
             return true;
         }
@@ -161,10 +159,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get selectedDoc() {
         return this.selectedDocumentView?.rootDoc;
     }
-    _selectedArray = new ObservableSet<Doc>();
-    clearSelectedArray = () => this._selectedArray.clear();
-    addToSelectedArray = (doc: Doc) => this._selectedArray.add(doc);
-    removeFromSelectedArray = (doc: Doc) => this._selectedArray.delete(doc);
+    clearSelectedArray = () => this.selectedArray.clear();
+    addToSelectedArray = (doc: Doc) => this.selectedArray.add(doc);
+    removeFromSelectedArray = (doc: Doc) => this.selectedArray.delete(doc);
 
     _unmounting = false;
     @action
@@ -448,7 +445,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.layoutDoc.presCollection = srcContext;
         }
         const presStatus = this.rootDoc.presStatus;
-        const selViewCache = Array.from(this._selectedArray);
+        const selViewCache = Array.from(this.selectedArray);
         const dragViewCache = Array.from(this._dragArray);
         const eleViewCache = Array.from(this._eleArray);
         const self = this;
@@ -756,27 +753,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     });
 
-    setMovementName = action((movement: any, activeItem: Doc): string => {
-        let output: string = 'none';
-        switch (movement) {
-            case PresMovement.Zoom:
-                output = 'Pan & Zoom';
-                break; //Pan and zoom
-            case PresMovement.Pan:
-                output = 'Pan';
-                break; //Pan
-            case PresMovement.Jump:
-                output = 'Jump cut';
-                break; //Jump Cut
-            case PresMovement.None:
-                output = 'None';
-                break; //None
-            default:
-                output = 'Zoom';
-                activeItem.presMovement = 'zoom';
-                break; //default set as zoom
+    movementName = action((activeItem: Doc) => {
+        if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) {
+            activeItem.presMovement = 'zoom';
         }
-        return output;
+        return StrCast(activeItem.presMovement);
     });
 
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
@@ -822,16 +803,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * For sorting the array so that the order is maintained when it is dropped.
      */
-    @action
-    sortArray = (): Doc[] => {
-        return this.childDocs.filter(doc => this._selectedArray.has(doc));
-    };
+    sortArray = () => this.childDocs.filter(doc => this.selectedArray.has(doc));
 
     /**
      * Method to get the list of selected items in the order in which they have been selected
      */
     @computed get listOfSelected() {
-        return Array.from(this._selectedArray).map((doc: Doc, index: any) => {
+        return Array.from(this.selectedArray).map((doc: Doc, index: any) => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
             if (curDoc && curDoc === this.activeItem)
@@ -874,24 +852,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //Command click
     @action
     multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
-        if (!this._selectedArray.has(doc)) {
+        if (!this.selectedArray.has(doc)) {
             this.addToSelectedArray(doc);
             this._eleArray.push(ref);
             this._dragArray.push(drag);
         } else {
             this.removeFromSelectedArray(doc);
-            this.removeFromArray(this._eleArray, doc);
-            this.removeFromArray(this._dragArray, doc);
+            this._eleArray.splice(this._eleArray.indexOf(ref));
+            this._dragArray.splice(this._dragArray.indexOf(drag));
         }
         this.selectPres();
     };
 
-    removeFromArray = (arr: any[], val: any) => {
-        const index: number = arr.indexOf(val);
-        const ret: any[] = arr.splice(index, 1);
-        arr = ret;
-    };
-
     //Shift click
     @action
     shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
@@ -924,9 +896,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
@@ -940,7 +910,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 if (this.layoutDoc.presStatus === 'edit') {
                     undoBatch(
                         action(() => {
-                            for (const doc of this._selectedArray) {
+                            for (const doc of this.selectedArray) {
                                 this.removeDocument(doc);
                             }
                             this.clearSelectedArray();
@@ -1027,13 +997,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    getAllIndexes = (arr: Doc[], val: Doc): number[] => {
-        const indexes = [];
-        for (let i = 0; i < arr.length; i++) {
-            arr[i] === val && indexes.push(i);
-        }
-        return indexes;
-    };
+    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
     @computed get order() {
@@ -1152,7 +1116,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 10000) timeInMS = 10000;
-        this._selectedArray.forEach(doc => (doc.presTransition = timeInMS));
+        this.selectedArray.forEach(doc => (doc.presTransition = timeInMS));
     };
 
     // Converts seconds to ms and updates presTransition
@@ -1161,7 +1125,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) scale += change;
         if (scale < 0.01) scale = 0.01;
         if (scale > 1.5) scale = 1.5;
-        this._selectedArray.forEach(doc => (doc.presZoom = scale));
+        this.selectedArray.forEach(doc => (doc.presZoom = scale));
     };
 
     // Converts seconds to ms and updates presDuration
@@ -1170,100 +1134,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 20000) timeInMS = 20000;
-        this._selectedArray.forEach(doc => (doc.presDuration = timeInMS));
+        this.selectedArray.forEach(doc => (doc.presDuration = timeInMS));
     };
 
     /**
      * When the movement dropdown is changes
      */
     @undoBatch
-    updateMovement = action((movement: any, all?: boolean) => {
-        (all ? this.childDocs : this._selectedArray).forEach(doc => {
-            switch (movement) {
-                case PresMovement.Zoom: //Pan and zoom
-                    doc.presMovement = PresMovement.Zoom;
-                    break;
-                case PresMovement.Pan: //Pan
-                    doc.presMovement = PresMovement.Pan;
-                    break;
-                case PresMovement.Jump: //Jump Cut
-                    doc.presJump = true;
-                    doc.presMovement = PresMovement.Jump;
-                    break;
-                case PresMovement.None:
-                default:
-                    doc.presMovement = PresMovement.None;
-                    break;
-            }
-        });
-    });
+    updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presMovement = movement)));
 
     @undoBatch
     @action
     updateHideBefore = (activeItem: Doc) => {
         activeItem.presHideBefore = !activeItem.presHideBefore;
-        this._selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
+        this.selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
     };
 
     @undoBatch
     @action
     updateHideAfter = (activeItem: Doc) => {
         activeItem.presHideAfter = !activeItem.presHideAfter;
-        this._selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
+        this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
     };
 
     @undoBatch
     @action
     updateOpenDoc = (activeItem: Doc) => {
         activeItem.openDocument = !activeItem.openDocument;
-        this._selectedArray.forEach(doc => {
-            doc.openDocument = activeItem.openDocument;
-        });
+        this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument));
     };
 
     @undoBatch
     @action
-    updateEffectDirection = (effect: any, all?: boolean) => {
-        (all ? this.childDocs : this._selectedArray).forEach(doc => {
-            const tagDoc = doc; // Cast(doc.presentationTargetDoc, Doc, null);
-            switch (effect) {
-                case PresEffect.Left:
-                    tagDoc.presEffectDirection = PresEffect.Left;
-                    break;
-                case PresEffect.Right:
-                    tagDoc.presEffectDirection = PresEffect.Right;
-                    break;
-                case PresEffect.Top:
-                    tagDoc.presEffectDirection = PresEffect.Top;
-                    break;
-                case PresEffect.Bottom:
-                    tagDoc.presEffectDirection = PresEffect.Bottom;
-                    break;
-                case PresEffect.Center:
-                default:
-                    tagDoc.presEffectDirection = PresEffect.Center;
-                    break;
-            }
-        });
-    };
+    updateEffectDirection = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect));
 
     @undoBatch
     @action
-    updateEffect = (effect: any, all?: boolean) => {
-        (all ? this.childDocs : this._selectedArray).forEach(doc => {
-            const tagDoc = doc; //Cast(doc.presentationTargetDoc, Doc, null);
-            //prettier-ignore
-            switch (effect) {
-                default:
-                case PresEffect.None:   tagDoc.presEffect = PresEffect.None; break;
-                case PresEffect.Bounce: tagDoc.presEffect = PresEffect.Bounce; break;
-                case PresEffect.Fade:   tagDoc.presEffect = PresEffect.Fade; break;
-                case PresEffect.Flip:   tagDoc.presEffect = PresEffect.Flip; break;
-                case PresEffect.Roll:   tagDoc.presEffect = PresEffect.Roll; break;
-                case PresEffect.Rotate: tagDoc.presEffect = PresEffect.Rotate; break;
-            }
-        });
-    };
+    updateEffect = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffect = effect));
 
     _batch: UndoManager.Batch | undefined = undefined;
 
@@ -1272,6 +1179,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         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 ${this.activeItem.presEffect === effect || (effect === PresEffect.None && !this.activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}>
+                {effect}
+            </div>
+        );
+        const presMovement = (movement: PresMovement) => (
+            <div className={`presBox-dropdownOption ${activeItem.presMovement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
+                {movement}
+            </div>
+        );
+        const presDirection = (diretion: PresEffect, icon: string, gridColumn: number, gridRow: number, opts: object) => {
+            const color = this.activeItem.presEffectDirection === diretion || (diretion === PresEffect.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black';
+            return (
+                <Tooltip title={<div className="dash-tooltip">{diretion}</div>}>
+                    <div
+                        style={{ ...opts, border: diretion === PresEffect.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
+                        onClick={() => this.updateEffectDirection(diretion)}>
+                        {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
+                    </div>
+                </Tooltip>
+            );
+        };
+        const inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void) => {
+            return (
+                <input
+                    type="range"
+                    step={step}
+                    min={min}
+                    max={max}
+                    value={value}
+                    className={`toolbar-slider ${active ? '' : 'none'}`}
+                    onPointerDown={() => (this._batch = UndoManager.StartBatch('pres slider'))}
+                    onPointerUp={() => this._batch?.end()}
+                    onChange={e => {
+                        e.stopPropagation();
+                        change(e.target.value);
+                    }}
+                />
+            );
+        };
         if (activeItem && targetDoc) {
             const type = targetDoc.type;
             const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
@@ -1282,13 +1229,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
             return (
                 <div
-                    className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
-                    onPointerDown={e => e.stopPropagation()}
-                    onPointerUp={e => e.stopPropagation()}
+                    className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
+                    onPointerDown={StopEvent}
+                    onPointerUp={StopEvent}
                     onClick={action(e => {
                         e.stopPropagation();
-                        this.openMovementDropdown = false;
-                        this.openEffectDropdown = false;
+                        this._openMovementDropdown = false;
+                        this._openEffectDropdown = false;
                     })}>
                     <div className="ribbon-box">
                         Movement
@@ -1301,24 +1248,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 className="presBox-dropdown"
                                 onClick={action(e => {
                                     e.stopPropagation();
-                                    this.openMovementDropdown = !this.openMovementDropdown;
+                                    this._openMovementDropdown = !this._openMovementDropdown;
                                 })}
-                                style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
-                                {this.setMovementName(activeItem.presMovement, 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={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? 'grid' : 'none' }}>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>
-                                        None
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>
-                                        Pan {'&'} Zoom
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>
-                                        Pan
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>
-                                        Jump cut
-                                    </div>
+                                style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                                {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' }}>
+                                    {presMovement(PresMovement.None)}
+                                    {presMovement(PresMovement.Zoom)}
+                                    {presMovement(PresMovement.Pan)}
+                                    {presMovement(PresMovement.Jump)}
                                 </div>
                             </div>
                         )}
@@ -1336,21 +1275,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </div>
                             </div>
                         </div>
-                        <input
-                            type="range"
-                            step="1"
-                            min="0"
-                            max="150"
-                            value={zoom}
-                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
-                            id="toolbar-slider"
-                            onPointerDown={() => (this._batch = UndoManager.StartBatch('presZoom'))}
-                            onPointerUp={() => this._batch?.end()}
-                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
-                                e.stopPropagation();
-                                this.setZoom(e.target.value);
-                            }}
-                        />
+                        {inputter('0', '1', '150', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)}
                         <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Movement Speed</div>
                             <div className="ribbon-property">
@@ -1365,21 +1290,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </div>
                             </div>
                         </div>
-                        <input
-                            type="range"
-                            step="0.1"
-                            min="0.1"
-                            max="10"
-                            value={transitionSpeed}
-                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
-                            id="toolbar-slider"
-                            onPointerDown={() => (this._batch = UndoManager.StartBatch('presTransition'))}
-                            onPointerUp={() => this._batch?.end()}
-                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
-                                e.stopPropagation();
-                                this.setTransitionTime(e.target.value);
-                            }}
-                        />
+                        {inputter('0.1', '0.1', '10', transitionSpeed, [PresMovement.Pan, PresMovement.Zoom].includes(activeItem.presMovement as any), this.setTransitionTime)}
                         <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}>
                             <div className="slider-text">Fast</div>
                             <div className="slider-text">Medium</div>
@@ -1390,35 +1301,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         Visibility {'&'} Duration
                         <div className="ribbon-doubleButton">
                             {isPresCollection ? null : (
-                                <Tooltip
-                                    title={
-                                        <>
-                                            <div className="dash-tooltip">{'Hide before presented'}</div>
-                                        </>
-                                    }>
+                                <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 after presented'}</div>
-                                        </>
-                                    }>
+                                <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>
-                                    </>
-                                }>
+                            <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)}>
                                     Lightbox
                                 </div>
@@ -1440,22 +1336,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         </div>
                                     </div>
                                 </div>
-                                <input
-                                    type="range"
-                                    step="0.1"
-                                    min="0.1"
-                                    max="20"
-                                    value={duration}
-                                    style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'block' }}
-                                    className={'toolbar-slider'}
-                                    id="duration-slider"
-                                    onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))}
-                                    onPointerUp={() => this._batch?.end()}
-                                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
-                                        e.stopPropagation();
-                                        this.setDurationTime(e.target.value);
-                                    }}
-                                />
+                                {inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)}
                                 <div className={'slider-headers'} style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'grid' }}>
                                     <div className="slider-text">Short</div>
                                     <div className="slider-text">Medium</div>
@@ -1471,98 +1352,30 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 className="presBox-dropdown"
                                 onClick={action(e => {
                                     e.stopPropagation();
-                                    this.openEffectDropdown = !this.openEffectDropdown;
+                                    this._openEffectDropdown = !this._openEffectDropdown;
                                 })}
-                                style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                                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()}>
-                                    <div
-                                        className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.None || !this.activeItem.presEffect ? 'active' : ''}`}
-                                        onPointerDown={e => e.stopPropagation()}
-                                        onClick={() => this.updateEffect(PresEffect.None)}>
-                                        None
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Fade ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>
-                                        Fade In
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Flip ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>
-                                        Flip
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Rotate ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>
-                                        Rotate
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Bounce ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>
-                                        Bounce
-                                    </div>
-                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Roll ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>
-                                        Roll
-                                    </div>
+                                <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 === 'None' ? 'none' : 'inline-flex' }}>
                                 <div className="presBox-subheading">Effect direction</div>
-                                <div className="ribbon-property">{this.effectDirection}</div>
+                                <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div>
                             </div>
                             <div className="effectDirection" style={{ display: effect === 'None' ? 'none' : 'grid', width: 40 }}>
-                                <Tooltip title={<div className="dash-tooltip">{'Enter from left'}</div>}>
-                                    <div
-                                        style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
-                                        onClick={() => this.updateEffectDirection(PresEffect.Left)}>
-                                        <FontAwesomeIcon icon={'angle-right'} />
-                                    </div>
-                                </Tooltip>
-                                <Tooltip title={<div className="dash-tooltip">{'Enter from right'}</div>}>
-                                    <div
-                                        style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
-                                        onClick={() => this.updateEffectDirection(PresEffect.Right)}>
-                                        <FontAwesomeIcon icon={'angle-left'} />
-                                    </div>
-                                </Tooltip>
-                                <Tooltip
-                                    title={
-                                        <>
-                                            <div className="dash-tooltip">{'Enter from top'}</div>
-                                        </>
-                                    }>
-                                    <div
-                                        style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
-                                        onClick={() => this.updateEffectDirection(PresEffect.Top)}>
-                                        <FontAwesomeIcon icon={'angle-down'} />
-                                    </div>
-                                </Tooltip>
-                                <Tooltip
-                                    title={
-                                        <>
-                                            <div className="dash-tooltip">{'Enter from bottom'}</div>
-                                        </>
-                                    }>
-                                    <div
-                                        style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
-                                        onClick={() => this.updateEffectDirection(PresEffect.Bottom)}>
-                                        <FontAwesomeIcon icon={'angle-up'} />
-                                    </div>
-                                </Tooltip>
-                                <Tooltip
-                                    title={
-                                        <>
-                                            <div className="dash-tooltip">{'Enter from center'}</div>
-                                        </>
-                                    }>
-                                    <div
-                                        style={{
-                                            gridColumn: 2,
-                                            gridRow: 2,
-                                            width: 10,
-                                            height: 10,
-                                            alignSelf: 'center',
-                                            justifySelf: 'center',
-                                            border: this.activeItem.presEffectDirection === PresEffect.Center || !this.activeItem.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : 'solid 2px black',
-                                            borderRadius: '100%',
-                                            cursor: 'pointer',
-                                        }}
-                                        onClick={() => this.updateEffectDirection(PresEffect.Center)}></div>
-                                </Tooltip>
+                                {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' })}
                             </div>
                         </div>
                     )}
@@ -1576,34 +1389,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     }
 
-    @computed get effectDirection() {
-        // prettier-ignore
-        switch (this.activeItem.presEffectDirection) {
-            case 'left': return 'Enter from left'; 
-            case 'right': return 'Enter from right'; 
-            case 'top':  return'Enter from top';
-            case 'bottom': return 'Enter from bottom';
-        }
-        return 'Enter from center';
-    }
-
     @undoBatch
     @action
     applyTo = (array: Doc[]) => {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        this.updateMovement(activeItem.presMovement, true);
-        this.updateEffect(activeItem.presEffect, true);
-        this.updateEffectDirection(activeItem.presEffectDirection, true);
-        array.forEach(doc => {
-            const curDoc = Cast(doc, Doc, null);
-            const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
-            if (tagDoc && targetDoc) {
-                curDoc.presTransition = activeItem.presTransition;
-                curDoc.presDuration = activeItem.presDuration;
-                curDoc.presHideBefore = activeItem.presHideBefore;
-                curDoc.presHideAfter = activeItem.presHideAfter;
-            }
+        this.updateMovement(this.activeItem.presMovement as PresMovement, true);
+        this.updateEffect(this.activeItem.presEffect as PresEffect, true);
+        this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffect, true);
+        const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem;
+        array.forEach(curDoc => {
+            curDoc.presTransition = presTransition;
+            curDoc.presDuration = presDuration;
+            curDoc.presHideBefore = presHideBefore;
+            curDoc.presHideAfter = presHideAfter;
         });
     };
 
@@ -1785,7 +1582,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return (
             <div
                 className={'presBox-toolbar-dropdown'}
-                style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
+                style={{ display: this._newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
                 onClick={e => e.stopPropagation()}
                 onPointerUp={e => e.stopPropagation()}
                 onPointerDown={e => e.stopPropagation()}>
@@ -1956,7 +1753,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view)
     @computed get presentDropdown() {
         return (
-            <div className={`dropdown-play ${this.presentTools ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+            <div className={`dropdown-play ${this._presentTools ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                 <div
                     className="dropdown-play-button"
                     onClick={undoBatch(
@@ -2042,7 +1839,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             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={`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
@@ -2155,18 +1952,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     switchActive = (color: ColorState) => {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        const val = String(color.hex);
-        targetDoc['pres-text-color'] = val;
+        this.targetDoc['pres-text-color'] = String(color.hex);
         return true;
     };
     @undoBatch
     @action
     switchPresented = (color: ColorState) => {
-        const targetDoc: Doc = this.targetDoc;
-        const val = String(color.hex);
-        targetDoc['pres-text-viewed-color'] = val;
+        this.targetDoc['pres-text-viewed-color'] = String(color.hex);
         return true;
     };
 
@@ -2228,12 +2020,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //Toggle whether the user edits or not
     @action
     editScrollProgressivize = (e: React.MouseEvent) => {
-        const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         if (!targetDoc.editScrollProgressivize) {
             if (!targetDoc.scrollProgressivize) {
                 targetDoc.scrollProgressivize = true;
-                activeItem.scrollProgressivize = true;
+                this.activeItem.scrollProgressivize = true;
             }
             targetDoc.editScrollProgressivize = true;
         } else {
@@ -2245,8 +2036,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     progressivizeScroll = (e: React.MouseEvent) => {
         e.stopPropagation();
-        const activeItem: Doc = this.activeItem;
-        activeItem.scrollProgressivize = !activeItem.scrollProgressivize;
+        this.activeItem.scrollProgressivize = !this.activeItem.scrollProgressivize;
         const targetDoc: Doc = this.targetDoc;
         targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize;
         // CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame));
@@ -2353,13 +2143,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     checkList = (doc: Doc, list: any): number => {
         const x: List<number> = list;
-        if (x && x.length >= NumCast(doc._currentFrame) + 1) {
+        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)];
-        } else return 100;
+        }
+        return 100;
     };
 
     @computed get progressivizeChildDocs() {
@@ -2413,26 +2204,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @action
-    nextAppearFrame = (doc: Doc, i: number): void => {
-        // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
-        // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
-        const appearFrame = Cast(doc.appearFrame, 'number', null);
-        if (appearFrame === undefined) {
-            doc.appearFrame = 0;
-        }
-        doc.appearFrame = appearFrame + 1;
+    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): void => {
-        // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
-        // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
-        const appearFrame = Cast(doc.appearFrame, 'number', null);
-        if (appearFrame === undefined) {
-            doc.appearFrame = 0;
-        }
-        doc.appearFrame = Math.max(0, appearFrame - 1);
+    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));
     };
 
@@ -2483,12 +2262,7 @@ 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 }}
                         className={'toolbar-button'}
@@ -2507,22 +2281,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </div>
                         </Tooltip>
                         <div className="toolbar-divider" /> */}
-                        <Tooltip
-                            title={
-                                <>
-                                    <div className="dash-tooltip">{presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}</div>
-                                </>
-                            }>
+                        <Tooltip title={<div className="dash-tooltip">{presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}</div>}>
                             <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
                                 <FontAwesomeIcon className={'toolbar-thumbtack'} icon={'keyboard'} style={{ color: presKeyEvents ? activeColor : inactiveColor }} />
                             </div>
                         </Tooltip>
-                        <Tooltip
-                            title={
-                                <>
-                                    <div className="dash-tooltip">{propTitle}</div>
-                                </>
-                            }>
+                        <Tooltip title={<div className="dash-tooltip">{propTitle}</div>}>
                             <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
                                 <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: SettingsManager.propertiesWidth > 0 ? activeColor : inactiveColor }} />
                             </div>
@@ -2545,14 +2309,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
                 {isMini ? null : (
                     <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
-                        <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>
+                        <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}>
                             List
                         </option>
-                        <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>
+                        <option onPointerDown={StopEvent} value={CollectionViewType.Tree}>
                             Tree
                         </option>
                         {Doc.noviceMode ? null : (
-                            <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>
+                            <option onPointerDown={StopEvent} value={CollectionViewType.Carousel3D}>
                                 3D Carousel
                             </option>
                         )}
@@ -2573,11 +2337,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         </div>
                         {mode === CollectionViewType.Carousel3D || isMini ? null : (
                             <div
-                                className={`presBox-button-right ${this.presentTools ? 'active' : ''}`}
+                                className={`presBox-button-right ${this._presentTools ? 'active' : ''}`}
                                 onClick={action(() => {
-                                    if (this.childDocs.length) this.presentTools = !this.presentTools;
+                                    if (this.childDocs.length) this._presentTools = !this._presentTools;
                                 })}>
-                                <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'angle-down'} />
+                                <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this._presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'angle-down'} />
                                 {this.presentDropdown}
                             </div>
                         )}
@@ -2595,7 +2359,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     updateList = (list: any): List<number> => {
         const targetDoc: Doc = this.targetDoc;
         const x: List<number> = list;
-        x.length + 1;
         x[x.length - 1] = NumCast(targetDoc._scrollY);
         return x;
     };
@@ -2603,8 +2366,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     newFrame = () => {
         const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        const type: string = StrCast(targetDoc.type);
+        const type: string = StrCast(this.targetDoc.type);
         if (!activeItem.frameList) activeItem.frameList = new List<number>();
         switch (type) {
             case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB:
@@ -2644,23 +2406,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     @computed get frameList() {
-        const activeItem: Doc = this.activeItem;
-        const frameList: List<number> = this.getList(activeItem.frameList);
-        if (frameList) {
-            const frameItems = frameList.map(value => <div className="framList-item"></div>);
-            return <div className="frameList-container">{frameItems}</div>;
-        } else return null;
+        const frameList: List<number> = this.getList(this.activeItem.frameList);
+        return !frameList ? null : (
+            <div className="frameList-container">
+                {frameList.map(value => (
+                    <div className="framList-item" />
+                ))}
+            </div>
+        );
     }
 
     @computed get playButtonFrames() {
         const targetDoc = this.targetDoc;
-        return this.targetDoc ? (
+        return !this.targetDoc ? null : (
             <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}>
                 <div>{NumCast(targetDoc._currentFrame)}</div>
                 <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
                 <div>{NumCast(targetDoc.lastFrame)}</div>
             </div>
-        ) : null;
+        );
     }
 
     @computed get playButtons() {
@@ -2674,7 +2438,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         <FontAwesomeIcon icon={'redo-alt'} />
                     </div>
                 </Tooltip>
-                <div className="presPanel-divider"></div>
+                <div className="presPanel-divider" />
                 <div
                     className="presPanel-button"
                     style={{ opacity: presStart ? 0.4 : 1 }}
@@ -2767,9 +2531,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     @action
-    startMarqueeCreateSlide = () => {
-        PresBox.startMarquee = true;
-    };
+    startMarqueeCreateSlide = () => (PresBox.startMarquee = true);
 
     AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
         var indexNum = 0;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index c78828a78..a00441ddc 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -50,7 +50,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return this.presBox.presStatus;
     }
     @computed get selectedArray() {
-        return this.presBoxView?._selectedArray;
+        return this.presBoxView?.selectedArray;
     }
     @computed get presBoxView() {
         const vpath = this.props.docViewPath();
diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts
index 93ab323fb..c6a222c3a 100644
--- a/src/client/views/nodes/trails/PresEnums.ts
+++ b/src/client/views/nodes/trails/PresEnums.ts
@@ -1,28 +1,28 @@
 export enum PresMovement {
-    Zoom = "zoom",
-    Pan = "pan",
-    Jump = "jump",
-    None = "none",
+    Zoom = 'zoom',
+    Pan = 'pan',
+    Jump = 'jump',
+    None = 'none',
 }
 
 export enum PresEffect {
-    Zoom = "Zoom",
-    Lightspeed = "Lightspeed",
-    Fade = "Fade in",
-    Flip = "Flip",
-    Rotate = "Rotate",
-    Bounce = "Bounce",
-    Roll = "Roll",
-    None = "None",
-    Left = "left",
-    Right = "right",
-    Center = "center",
-    Top = "top",
-    Bottom = "bottom"
+    Zoom = 'Zoom',
+    Lightspeed = 'Lightspeed',
+    Fade = 'Fade in',
+    Flip = 'Flip',
+    Rotate = 'Rotate',
+    Bounce = 'Bounce',
+    Roll = 'Roll',
+    None = 'None',
+    Left = 'Enter from left',
+    Right = 'Enter from right',
+    Center = 'Enter from center',
+    Top = 'Enter from Top',
+    Bottom = 'Enter from bottom',
 }
 
 export enum PresStatus {
-    Autoplay = "auto",
-    Manual = "manual",
-    Edit = "edit"
-}
\ No newline at end of file
+    Autoplay = 'auto',
+    Manual = 'manual',
+    Edit = 'edit',
+}
-- 
cgit v1.2.3-70-g09d2


From 35d5458347a10b9af9bcf65e5fddbb92d842c8b1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 1 Sep 2022 16:26:01 -0400
Subject: cleanup preselementbox a little.

---
 src/client/views/nodes/trails/PresElementBox.tsx | 129 ++++++-----------------
 1 file changed, 35 insertions(+), 94 deletions(-)

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

diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index a00441ddc..e4fab7528 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -81,9 +81,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
 
     @action
-    presExpandDocumentClick = () => {
-        this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton;
-    };
+    presExpandDocumentClick = () => (this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton);
 
     embedHeight = (): number => 97;
     // embedWidth = () => this.props.PanelWidth();
@@ -385,7 +383,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     hideRecording = (e: React.MouseEvent, iconClick: boolean = false) => {
         e.stopPropagation();
         this.removeAllRecordingInOverlay();
-
         // if (iconClick) PresElementBox.showVideo = false;
     };
 
@@ -405,7 +402,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     @action
-    startRecording = (activeItem: Doc) => {
+    startRecording = (e: React.MouseEvent, activeItem: Doc) => {
+        e.stopPropagation();
         console.log('start recording', 'activeItem', activeItem);
         if (PresElementBox.videoIsRecorded(activeItem)) {
             // if we already have an existing recording
@@ -481,22 +479,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
                 onPointerDown={this.headerDown}>
-                {/* {miniView ?
-                              // when width is LESS than 110 px 
-                              <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
-                                    {`${this.indexInPres + 1}.`}
-                              </div>
-                              :
-                              // when width is MORE than 110 px 
-                              <div className="presItem-number">
-                                    {`${this.indexInPres + 1}.`}
-                              </div>} */}
-                {/* <div className="presItem-number">
-                              {`${this.indexInPres + 1}.`}
-                        </div> */}
-                {miniView ? null : (
+                {miniView ? (
+                    <div className={`presItem-miniSlide ${isSelected ? 'active' : ''}`} ref={this._dragRef}>
+                        {`${this.indexInPres + 1}.`}
+                    </div>
+                ) : (
                     <div
-                        ref={miniView ? null : this._dragRef}
+                        ref={this._dragRef}
                         className={`presItem-slide ${isSelected ? 'active' : ''}`}
                         style={{
                             backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
@@ -514,79 +503,37 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         </div>
                         {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
                         {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
-                        <div className={'presItem-slideButtons'}>
-                            <Tooltip
-                                title={
-                                    <>
-                                        <div className="dash-tooltip">{'Update view'}</div>
-                                    </>
-                                }>
+                        <div className="presItem-slideButtons">
+                            <Tooltip title={<div className="dash-tooltip">Update view</div>}>
                                 <div className="slideButton" onClick={() => this.updateView(targetDoc, activeItem)} style={{ fontWeight: 700, display: activeItem.presPinView ? 'flex' : 'none' }}>
                                     V
                                 </div>
                             </Tooltip>
-
-                            {this.recordingIsInOverlay ? (
-                                <Tooltip
-                                    title={
-                                        <>
-                                            <div className="dash-tooltip">{'Hide Recording'}</div>
-                                        </>
-                                    }>
-                                    <div className="slideButton" onClick={e => this.hideRecording(e, true)} style={{ fontWeight: 700 }}>
-                                        <FontAwesomeIcon icon={'video-slash'} onPointerDown={e => e.stopPropagation()} />
-                                    </div>
-                                </Tooltip>
-                            ) : (
-                                <Tooltip
-                                    title={
-                                        <>
-                                            <div className="dash-tooltip">{`${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>
-                                        </>
-                                    }>
-                                    <div
-                                        className="slideButton"
-                                        onClick={e => {
-                                            e.stopPropagation();
-                                            this.startRecording(activeItem);
-                                        }}
-                                        style={{ fontWeight: 700 }}>
-                                        <FontAwesomeIcon icon={'video'} onPointerDown={e => e.stopPropagation()} />
-                                    </div>
-                                </Tooltip>
-                            )}
-
-                            {this.indexInPres === 0 ? null : (
-                                <Tooltip
-                                    title={
-                                        <>
-                                            <div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>
-                                        </>
-                                    }>
-                                    <div
-                                        className="slideButton"
-                                        onClick={() => (activeItem.groupWithUp = !activeItem.groupWithUp)}
-                                        style={{
-                                            zIndex: 1000 - this.indexInPres,
-                                            fontWeight: 700,
-                                            backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
-                                            height: activeItem.groupWithUp ? 53 : 18,
-                                            transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
-                                        }}>
-                                        <div style={{ transform: activeItem.groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
-                                            <FontAwesomeIcon icon={'arrow-up'} onPointerDown={e => e.stopPropagation()} />
-                                        </div>
+                            <Tooltip title={<div className="dash-tooltip">{this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
+                                <div className="slideButton" onClick={e => (this.recordingIsInOverlay ? this.hideRecording(e, true) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}>
+                                    <FontAwesomeIcon icon={`video${this.recordingIsInOverlay ? '-slash' : ''}`} onPointerDown={e => e.stopPropagation()} />
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>}>
+                                <div
+                                    className="slideButton"
+                                    onClick={() => (activeItem.groupWithUp = !activeItem.groupWithUp)}
+                                    style={{
+                                        display: this.indexInPres === 0 ? 'none' : '',
+                                        zIndex: 1000 - this.indexInPres,
+                                        fontWeight: 700,
+                                        backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined,
+                                        height: activeItem.groupWithUp ? 53 : 18,
+                                        transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined,
+                                    }}>
+                                    <div style={{ transform: activeItem.groupWithUp ? 'rotate(180deg) translate(0, -17.5px)' : 'rotate(0deg)' }}>
+                                        <FontAwesomeIcon icon={'arrow-up'} onPointerDown={e => e.stopPropagation()} />
                                     </div>
-                                </Tooltip>
-                            )}
-                            <Tooltip
-                                title={
-                                    <>
-                                        <div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>
-                                    </>
-                                }>
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>}>
                                 <div
-                                    className={'slideButton'}
+                                    className="slideButton"
                                     onClick={e => {
                                         e.stopPropagation();
                                         this.presExpandDocumentClick();
@@ -594,18 +541,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
                                 </div>
                             </Tooltip>
-                            <Tooltip
-                                title={
-                                    <>
-                                        <div className="dash-tooltip">{'Remove from presentation'}</div>
-                                    </>
-                                }>
+                            <Tooltip title={<div className="dash-tooltip">Remove from presentation</div>}>
                                 <div className={'slideButton'} onClick={this.removeItem}>
                                     <FontAwesomeIcon icon={'trash'} onPointerDown={e => e.stopPropagation()} />
                                 </div>
                             </Tooltip>
                         </div>
-                        {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
                         {this.renderEmbeddedInline}
                     </div>
                 )}
-- 
cgit v1.2.3-70-g09d2


From 0454279c9845fc64dc2c77aeb6bca9a5fee573f6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 8 Sep 2022 13:34:55 -0400
Subject: added rotation to preselement saved parameters.

---
 src/client/views/nodes/trails/PresBox.tsx        | 2 ++
 src/client/views/nodes/trails/PresElementBox.tsx | 1 +
 2 files changed, 3 insertions(+)

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

diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 1439ae5d5..beebca973 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -407,6 +407,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || textview || pinProps.activeFrame !== undefined;
             pinDoc.presX = NumCast(targetDoc.x);
             pinDoc.presY = NumCast(targetDoc.y);
+            pinDoc.presRot = NumCast(targetDoc.jitterRotation);
             pinDoc.presWidth = NumCast(targetDoc.width);
             pinDoc.presHeight = NumCast(targetDoc.height);
 
@@ -487,6 +488,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             targetDoc._dataTransition = presTransitionTime;
             targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x));
             targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y));
+            targetDoc.jitterRotation = NumCast(activeItem.presRot, NumCast(targetDoc.jitterRotation));
             targetDoc.width = NumCast(activeItem.presWidth, NumCast(targetDoc.width));
             targetDoc.height = NumCast(activeItem.presHeight, NumCast(targetDoc.height));
             setTimeout(() => (targetDoc._dataTransition = undefined), transTime + 10);
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index e4fab7528..d88ff45fa 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -337,6 +337,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
         activeItem.presX = NumCast(targetDoc.x);
         activeItem.presY = NumCast(targetDoc.y);
+        activeItem.presRot = NumCast(targetDoc.jitterRotation);
         activeItem.presWidth = NumCast(targetDoc.width);
         activeItem.presHeight = NumCast(targetDoc.height);
     };
-- 
cgit v1.2.3-70-g09d2

