From 513dcaa2d8c86f1cb5236ce89062cace6f418d1b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 10 Aug 2022 14:52:04 -0400
Subject: cleaned up pinning documents with an activeFrame index (and special
 case of isInkMask).  removed text-color from pres boxes which was breaking
 opacity when docs had an appearFrame > 0.

---
 src/client/views/collections/TabDocView.tsx | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index ec291e72c..f30faab79 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -288,14 +288,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
                 }
                 //save position
-                if (pinProps?.setPosition || pinDoc.isInkMask) {
-                    pinDoc.setPosition = true;
-                    pinDoc.y = doc.y;
-                    pinDoc.x = doc.x;
-                    pinDoc.presHideAfter = true;
-                    pinDoc.presHideBefore = true;
+                if (pinProps?.activeFrame !== undefined) {
+                    pinDoc.presActiveFrame = pinProps?.activeFrame;
                     pinDoc.title = doc.title + ' (move)';
-                    pinDoc.presMovement = PresMovement.None;
+                    pinDoc.presMovement = PresMovement.Pan;
+                    if (pinDoc.isInkMask) {
+                        pinDoc.presHideAfter = true;
+                        pinDoc.presHideBefore = true;
+                        pinDoc.presMovement = PresMovement.None;
+                    }
                 }
                 if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
                 PresBox.Instance?._selectedArray.clear();
-- 
cgit v1.2.3-70-g09d2


From 081632757af3c2ec2e4482c1e5fe710c8ee6cad8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 11 Aug 2022 19:01:37 -0400
Subject: added better ui and backend support for mask ink strokes. added frame
 #'s ui for seeing current animation frame.

---
 src/client/documents/Documents.ts                  | 19 ++++++++--
 src/client/util/CurrentUserUtils.ts                |  6 ++--
 src/client/util/DragManager.ts                     | 14 ++++----
 src/client/views/InkingStroke.tsx                  | 28 +++++++++++----
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/collections/CollectionMenu.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        | 10 +++---
 .../CollectionFreeFormLayoutEngines.tsx            |  1 +
 .../collectionFreeForm/CollectionFreeFormView.scss | 23 +++++++++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  9 +++--
 src/client/views/global/globalCssVariables.scss    | 13 +++----
 .../views/global/globalCssVariables.scss.d.ts      |  6 ++--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  7 ++--
 src/client/views/nodes/DocumentView.tsx            |  6 ++--
 src/client/views/nodes/button/FontIconBox.tsx      | 34 +++++++++++++-----
 src/client/views/nodes/trails/PresBox.tsx          | 36 +++++++++++--------
 src/fields/InkField.ts                             | 40 +++++++++++-----------
 17 files changed, 162 insertions(+), 93 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c7ea04839..cf9ed43e1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -31,7 +31,7 @@ import { CollectionView } from '../views/collections/CollectionView';
 import { ContextMenu } from '../views/ContextMenu';
 import { ContextMenuProps } from '../views/ContextMenuItem';
 import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss';
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from '../views/InkingStroke';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke';
 import { AudioBox } from '../views/nodes/AudioBox';
 import { FontIconBox } from '../views/nodes/button/FontIconBox';
 import { ColorBox } from '../views/nodes/ColorBox';
@@ -918,9 +918,22 @@ export namespace Docs {
             return linkDoc;
         }
 
-        export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], options: DocumentOptions = {}) {
+        export function InkDocument(
+            color: string,
+            tool: string,
+            strokeWidth: number,
+            strokeBezier: string,
+            fillColor: string,
+            arrowStart: string,
+            arrowEnd: string,
+            dash: string,
+            points: PointData[],
+            isInkMask: boolean,
+            options: DocumentOptions = {}
+        ) {
             const I = new Doc();
             I[Initializing] = true;
+            I.isInkMask = isInkMask;
             I.type = DocumentType.INK;
             I.layout = InkingStroke.LayoutString('data');
             I.color = color;
@@ -1424,7 +1437,7 @@ export namespace DocUtils {
             created = Docs.Create.RecordingDocument(field.url.href, resolved);
             layout = RecordingBox.LayoutString;
         } else if (field instanceof InkField) {
-            created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved);
             layout = InkingStroke.LayoutString;
         } else if (field instanceof List && field[0] instanceof Doc) {
             created = Docs.Create.StackingDocument(DocListCast(field), resolved);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d19874720..dcf4a71c8 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -645,8 +645,9 @@ export class CurrentUserUtils {
             // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} },
             { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle",                      scripts: {onClick:'{ return setActiveTool("circle", _readOnly_);}'} },
             // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveTool("square")' },
-            { title: "Line",   toolTip: "Line (Ctrl+Shift+L)",   btnType: ButtonType.ToggleButton, icon: "minus",                       scripts: {onClick: '{ return setActiveTool("line", _readOnly_);}' }},
-            { title: "Fill",   toolTip: "Fill color",            btnType: ButtonType.ColorButton,  icon: "fill-drip",ignoreClick: true, scripts: {script: "{ return setFillColor(value, _readOnly_);}"} },
+            { title: "Line",   toolTip: "Line (Ctrl+Shift+L)",   btnType: ButtonType.ToggleButton, icon: "minus",                       scripts: {onClick:'{ return setActiveTool("line", _readOnly_);}' }},
+            { title: "Mask",   toolTip: "Mask",                  btnType: ButtonType.ToggleButton, icon: "user-circle",                 scripts: {onClick:'{ return setIsInkMask(_readOnly_);}'} },
+            { title: "Fill",   toolTip: "Fill color",            btnType: ButtonType.ColorButton,  icon: "fill-drip",ignoreClick: true, scripts: {script: '{ return setFillColor(value, _readOnly_);}'} },
             { title: "Width",  toolTip: "Stroke width",          btnType: ButtonType.NumberButton,                   ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1},
             { title: "Color",  toolTip: "Stroke color",          btnType: ButtonType.ColorButton,  icon: "pen",      ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} },
         ];
@@ -674,6 +675,7 @@ export class CurrentUserUtils {
                     CollectionViewType.Grid, CollectionViewType.NoteTaking]),
               title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                    ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
             { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
+            { title: "Num",     icon: "",           toolTip: "Frame Number",         btnType: ButtonType.TextButton,          funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20,  scripts: {}},
             { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
             { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         funcs: {hidden: '!SelectionManager_selectedDocType()'},                     ignoreClick: true, width: 20,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected
             { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         funcs: {hidden: '!SelectionManager_selectedDocType()'},                     ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}},
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ccd94c56e..d781a87ab 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -377,14 +377,14 @@ export namespace DragManager {
             }
             const rect = ele.getBoundingClientRect();
             const scaleX = rect.width / (ele.offsetWidth || rect.width);
-            const scaleY = ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX;
+            const scaleY = scaleX; //ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX;
 
             elesCont.left = Math.min(rect.left, elesCont.left);
             elesCont.top = Math.min(rect.top, elesCont.top);
             elesCont.right = Math.max(rect.right, elesCont.right);
             elesCont.bottom = Math.max(rect.bottom, elesCont.bottom);
-            xs.push(rect.left);
-            ys.push(rect.top);
+            xs.push(rect.left + (options?.offsetX || 0));
+            ys.push(rect.top + (options?.offsetY || 0));
             scaleXs.push(scaleX);
             scaleYs.push(scaleY);
             Object.assign(dragElement.style, {
@@ -401,9 +401,9 @@ export namespace DragManager {
                 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})`,
+                transform: `translate(${xs[0]}px, ${ys[0]}px) scale(${scaleX}, ${scaleY})`,
             });
-            dragLabel.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0) - 20}px)`;
+            dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`;
 
             if (docsToDrag.length) {
                 const pdfBox = dragElement.getElementsByTagName('canvas');
@@ -543,8 +543,8 @@ export namespace DragManager {
             const moveVec = { x: x - lastPt.x, y: y - lastPt.y };
             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)}px, ${(ys[i] += moveVec.y)}px)  scale(${scaleXs[i]}, ${scaleYs[i]})`));
+            dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`;
         };
         const upHandler = (e: PointerEvent) => {
             clearTimeout(startWindowDragTimer);
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index e5de7a0c5..2671aea56 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -23,9 +23,9 @@
 import React = require('react');
 import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, WidthSym } from '../../fields/Doc';
+import { Doc, HeightSym, WidthSym } from '../../fields/Doc';
 import { InkData, InkField, InkTool } from '../../fields/InkField';
-import { Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { CognitiveServices } from '../cognitive_services/CognitiveServices';
@@ -42,12 +42,13 @@ import { InkTangentHandles } from './InkTangentHandles';
 import { DocComponentView } from './nodes/DocumentView';
 import { FieldView, FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { INK_MASK_SIZE } from './global/globalCssVariables.scss';
 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)
+    static readonly MaskDim = INK_MASK_SIZE; // 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);
     }
@@ -353,9 +354,18 @@ 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 isInkMask = BoolCast(this.layoutDoc.isInkMask);
+        const fillColor = isInkMask ? '#aaaaaa' : StrCast(this.layoutDoc.fillColor, 'transparent');
         const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : StrCast(this.layoutDoc.color);
 
+        // bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be.
+        if (isInkMask && (this.layoutDoc[WidthSym]() !== Math.round(this.layoutDoc[WidthSym]()) || this.layoutDoc[HeightSym]() !== Math.round(this.layoutDoc[HeightSym]()))) {
+            setTimeout(() => {
+                this.layoutDoc._width = Math.round(NumCast(this.layoutDoc[WidthSym]()));
+                this.layoutDoc._height = Math.round(NumCast(this.layoutDoc[HeightSym]()));
+            });
+        }
+
         // Visually renders the polygonal line made by the user.
         const inkLine = InteractionUtils.CreatePolyline(
             inkData,
@@ -430,7 +440,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                     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',
+                        // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
                         cursor: this.props.isSelected() ? 'default' : undefined,
                     }}
                     {...(!closed ? interactions : {})}>
@@ -467,7 +477,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
                         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',
+                            mixBlendMode: 'unset',
                             cursor: this.props.isSelected() ? 'default' : undefined,
                             position: 'absolute',
                         }}
@@ -489,6 +499,9 @@ export function SetActiveBezierApprox(bezier: string): void {
 export function SetActiveInkColor(value: string) {
     ActiveInkPen() && (ActiveInkPen().activeInkColor = value);
 }
+export function SetActiveIsInkMask(value: boolean) {
+    ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value);
+}
 export function SetActiveFillColor(value: string) {
     ActiveInkPen() && (ActiveInkPen().activeFillColor = value);
 }
@@ -513,6 +526,9 @@ export function ActiveInkColor(): string {
 export function ActiveFillColor(): string {
     return StrCast(ActiveInkPen()?.activeFillColor, '');
 }
+export function ActiveIsInkMask(): boolean {
+    return BoolCast(ActiveInkPen()?.activeIsInkMask, false);
+}
 export function ActiveArrowStart(): string {
     return StrCast(ActiveInkPen()?.activeArrowStart, '');
 }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e96f65548..c166594e5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -457,6 +457,7 @@ export class MainView extends React.Component {
                 fa.faVolumeDown,
                 fa.faSquareRootAlt,
                 fa.faVolumeMute,
+                fa.faUserCircle,
             ]
         );
         this.initAuthenticationRouters();
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 8432619de..eb55650e4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1025,7 +1025,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                                         <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
                                     </div>
                                 </Tooltip>
-                                <Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</div>} placement="bottom">
+                                <Tooltip key="num" title={<div className="dash-tooltip">Frame number</div>} placement="bottom">
                                     <div
                                         className="numKeyframe"
                                         style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? 'white' : 'black', backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? '#5B9FDD' : '#AEDDF8' }}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f30faab79..ff4b1e2ce 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -292,11 +292,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     pinDoc.presActiveFrame = pinProps?.activeFrame;
                     pinDoc.title = doc.title + ' (move)';
                     pinDoc.presMovement = PresMovement.Pan;
-                    if (pinDoc.isInkMask) {
-                        pinDoc.presHideAfter = true;
-                        pinDoc.presHideBefore = true;
-                        pinDoc.presMovement = PresMovement.None;
-                    }
+                }
+                if (pinDoc.isInkMask) {
+                    pinDoc.presHideAfter = true;
+                    pinDoc.presHideBefore = true;
+                    pinDoc.presMovement = PresMovement.None;
                 }
                 if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
                 PresBox.Instance?._selectedArray.clear();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 3d85d32a0..ee01c341b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -45,6 +45,7 @@ export interface PoolData {
 export interface ViewDefResult {
     ele: JSX.Element;
     bounds?: ViewDefBounds;
+    inkMask?: boolean;
 }
 function toLabel(target: FieldResult<Field>) {
     if (typeof target === 'number' || Number(target)) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 79e063f7f..010132aa5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,4 +1,4 @@
-@import "../../global/globalCssVariables";
+@import '../../global/globalCssVariables';
 
 .collectionfreeformview-none {
     position: inherit;
@@ -20,8 +20,19 @@
     pointer-events: none;
 }
 
+.collectionfreeformview-mask {
+    mix-blend-mode: multiply;
+    z-index: 5000;
+    width: $INK_MASK_SIZE;
+    height: $INK_MASK_SIZE;
+    transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF);
+    background-color: rgba(0, 0, 0, 0.7);
+    pointer-events: none;
+    position: absolute;
+}
+
 .collectionfreeformview-viewdef {
-    >.collectionFreeFormDocumentView-container {
+    > .collectionFreeFormDocumentView-container {
         pointer-events: none;
 
         .contentFittingDocumentDocumentView-previewDoc {
@@ -210,13 +221,13 @@
         }
     }
 
-    .collectionfreeformview>.jsx-parser {
+    .collectionfreeformview > .jsx-parser {
         position: inherit;
         height: 100%;
         width: 100%;
     }
 
-    >.jsx-parser {
+    > .jsx-parser {
         z-index: 0;
     }
 
@@ -268,6 +279,6 @@
 
 .pullpane-indicator {
     z-index: 99999;
-    background-color: rgba($color: #000000, $alpha: .4);
+    background-color: rgba($color: #000000, $alpha: 0.4);
     position: absolute;
-}
\ 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 82b377dfa..f3074543b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -34,7 +34,7 @@ import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariable
 import { Timeline } from '../../animationtimeline/Timeline';
 import { ContextMenu } from '../../ContextMenu';
 import { GestureOverlay } from '../../GestureOverlay';
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
 import { LightboxView } from '../../LightboxView';
 import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from '../../nodes/DocumentView';
@@ -121,7 +121,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
 
     @computed get views() {
-        return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
+        const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask).map(ele => ele.ele);
+        const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && !ele.inkMask).map(ele => ele.ele);
+        if (viewsMask.length) renderableEles.push(<div className="collectionfreeformview-mask">{viewsMask}</div>);
+        return renderableEles;
     }
     @computed get fitToContentVals() {
         return {
@@ -579,6 +582,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     ActiveArrowEnd(),
                     ActiveDash(),
                     points,
+                    ActiveIsInkMask(),
                     {
                         title: 'ink stroke',
                         x: B.x - ActiveInkWidth() / 2,
@@ -1519,6 +1523,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 elements.push({
                     ele: this.getChildDocView(entry[1]),
                     bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica),
+                    inkMask: BoolCast(entry[1].pair.layout.isInkMask),
                 })
             );
 
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index a14634bdc..c5968d9a7 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -1,4 +1,4 @@
-@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
+@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
 // colors
 $white: #ffffff;
 $off-white: #fdfdfd;
@@ -16,8 +16,10 @@ $yellow: #f5d747;
 
 $close-red: #e48282;
 
-$drop-shadow: "#32323215";
+$drop-shadow: '#32323215';
 
+$INK_MASK_SIZE: 50000;
+$INK_MASK_SIZE_HALF: -25000px;
 
 //padding
 $minimum-padding: 4px;
@@ -28,8 +30,7 @@ $large-padding: 32px;
 $icon-size: 28px;
 
 // fonts
-$sans-serif: "Roboto",
-sans-serif;
+$sans-serif: 'Roboto', sans-serif;
 $large-header: 16px;
 $body-text: 12px;
 $small-text: 9px;
@@ -54,7 +55,6 @@ $standard-border-radius: 3px;
 // shadow
 $standard-box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
 
-
 $dashboardselector-height: 32px;
 $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
 $docDecorations-zindex: 998; // then doc decorations appear over everything else
@@ -79,4 +79,5 @@ $TREE_BULLET_WIDTH: 20px;
     DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM;
     LEFT_MENU_WIDTH: $LEFT_MENU_WIDTH;
     TREE_BULLET_WIDTH: $TREE_BULLET_WIDTH;
-}
\ No newline at end of file
+    INK_MASK_SIZE: $INK_MASK_SIZE;
+}
diff --git a/src/client/views/global/globalCssVariables.scss.d.ts b/src/client/views/global/globalCssVariables.scss.d.ts
index 59c2b3585..76259113c 100644
--- a/src/client/views/global/globalCssVariables.scss.d.ts
+++ b/src/client/views/global/globalCssVariables.scss.d.ts
@@ -1,6 +1,5 @@
-
 interface IGlobalScss {
-    contextMenuZindex: string;  // context menu shows up over everything
+    contextMenuZindex: string; // context menu shows up over everything
     SCHEMA_DIVIDER_WIDTH: string;
     COLLECTION_BORDER_WIDTH: string;
     MINIMIZED_ICON_SIZE: string;
@@ -11,7 +10,8 @@ interface IGlobalScss {
     DFLT_IMAGE_NATIVE_DIM: string;
     LEFT_MENU_WIDTH: string;
     TREE_BULLET_WIDTH: string;
+    INK_MASK_SIZE: number;
 }
 declare const globalCssVariables: IGlobalScss;
 
-export = globalCssVariables;
\ No newline at end of file
+export = globalCssVariables;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index e154e8445..78f351f4f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -38,11 +38,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     get displayName() {
         return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')';
     } // this makes mobx trace() statements more descriptive
-    get maskCentering() {
-        return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0;
-    }
     get transform() {
-        return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`;
+        return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`;
     }
     get X() {
         return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x);
@@ -194,7 +191,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelHeight: this.panelHeight,
         };
         const background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const mixBlendMode = (StrCast(this.layoutDoc.mixBlendMode) as any) || (typeof background === 'string' && background && !background.startsWith('linear') && DashColor(background).alpha() !== 1 ? 'multiply' : undefined);
+        const mixBlendMode = undefined; // (StrCast(this.layoutDoc.mixBlendMode) as any) || (typeof background === 'string' && background && !background.startsWith('linear') && DashColor(background).alpha() !== 1 ? 'multiply' : undefined);
         return (
             <div
                 className={'collectionFreeFormDocumentView-container'}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f9ef85595..bcc55eab4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1640,8 +1640,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     render() {
         TraceMobx();
-        const xshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
-        const yshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
+        const xshift = () => (Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
+        const yshift = () => (Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
         const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
         const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
         return (
@@ -1652,7 +1652,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         ref={this.ContentRef}
                         style={{
                             transition: this.props.dataTransition,
-                            position: this.props.Document.isInkMask ? 'absolute' : undefined,
+                            //position: this.props.Document.isInkMask ? 'absolute' : undefined,
                             transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
                             width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
                             height:
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d3b95e25a..cb68c1ac3 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,6 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
+import Color from 'color';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -21,7 +22,7 @@ import { DocComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
 import { GestureOverlay } from '../../GestureOverlay';
 import { Colors } from '../../global/globalEnums';
-import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../../InkingStroke';
 import { InkTranscription } from '../../InkTranscription';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
@@ -498,8 +499,6 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 </div>
             );
 
-        const buttonText = StrCast(this.rootDoc.buttonText);
-
         // TODO:glr Add label of button type
         let button: JSX.Element | null = this.defaultButton;
 
@@ -508,7 +507,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 button = (
                     <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                        {buttonText ? <div className="button-text">{buttonText}</div> : null}
+                        {StrCast(this.rootDoc.buttonText) ? <div className="button-text">{StrCast(this.rootDoc.buttonText)}</div> : null}
                         {label}
                     </div>
                 );
@@ -563,11 +562,13 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
         }
 
-        return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? (
-            button
-        ) : button !== null ? (
-            <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>
-        ) : null;
+        const retval =
+            !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? (
+                button
+            ) : button !== null ? (
+                <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>
+            ) : null;
+        return retval;
     }
 }
 
@@ -860,6 +861,21 @@ ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean)
     }
 });
 
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setIsInkMask(checkResult?: boolean) {
+    const selected = SelectionManager.Docs().lastElement();
+    if (checkResult) {
+        if (selected?.type === DocumentType.INK) {
+            return BoolCast(selected.isInkMask) ? Colors.MEDIUM_BLUE : 'transparent';
+        }
+        return ActiveIsInkMask() ? Colors.MEDIUM_BLUE : 'transparent';
+    }
+    SetActiveIsInkMask(!ActiveIsInkMask());
+    SelectionManager.Docs()
+        .filter(doc => doc.type === DocumentType.INK)
+        .map(doc => (doc.isInkMask = !doc.isInkMask));
+});
+
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 3bbdce1e4..627f35e71 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -313,10 +313,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //it'll also execute the necessary actions if presentation is playing.
     public gotoDocument = action((index: number, from?: Doc, group?: boolean) => {
         Doc.UnBrushAllDocs();
+
         if (index >= 0 && index < this.childDocs.length) {
             this.rootDoc._itemIndex = index;
             const activeItem: Doc = this.activeItem;
             const targetDoc: Doc = this.targetDoc;
+            if (activeItem.presActiveFrame !== undefined) {
+                const context = DocCast(DocCast(activeItem.presentationTargetDoc).context);
+                context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(activeItem.presActiveFrame));
+            }
             if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
                 DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
             }
@@ -516,20 +521,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (tagDoc === this.layoutDoc.presCollection) {
                 tagDoc.opacity = 1;
             } else {
-                if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) {
-                } else if (curDoc.presHideBefore) {
-                    if (index > this.itemIndex) {
-                        tagDoc.opacity = 0;
-                    } else if (index === this.itemIndex || !curDoc.presHideAfter) {
+                if (curDoc.presHideBefore) {
+                    if (itemIndexes.length > 1 && curInd !== 0) {
                         tagDoc.opacity = 1;
+                    } else {
+                        if (index > this.itemIndex) {
+                            tagDoc.opacity = 0;
+                        } else if (index === this.itemIndex || !curDoc.presHideAfter) {
+                            tagDoc.opacity = 1;
+                        }
                     }
                 }
-                if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== itemIndexes.length - 1) {
-                } else if (curDoc.presHideAfter) {
-                    if (index < this.itemIndex) {
-                        tagDoc.opacity = 0;
-                    } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+                if (curDoc.presHideAfter) {
+                    if (itemIndexes.length > 1 && curInd !== itemIndexes.length - 1) {
                         tagDoc.opacity = 1;
+                    } else {
+                        if (index < this.itemIndex) {
+                            tagDoc.opacity = 0;
+                        } else if (index === this.itemIndex || !curDoc.presHideBefore) {
+                            tagDoc.opacity = 1;
+                        }
                     }
                 }
             }
@@ -821,11 +832,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
         if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
         else this.updateCurrentPresentation(context);
-
-        if (this.activeItem.presActiveFrame !== undefined) {
-            const context = DocCast(DocCast(this.activeItem.presentationTargetDoc).context);
-            context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(this.activeItem.presActiveFrame));
-        }
     };
 
     //Command click
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 114d5fc2f..a074098c1 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -1,22 +1,21 @@
-import { Bezier } from "bezier-js";
-import { createSimpleSchema, list, object, serializable } from "serializr";
-import { ScriptingGlobals } from "../client/util/ScriptingGlobals";
-import { Deserializable } from "../client/util/SerializationHelper";
-import { Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { ObjectField } from "./ObjectField";
+import { Bezier } from 'bezier-js';
+import { createSimpleSchema, list, object, serializable } from 'serializr';
+import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
+import { Deserializable } from '../client/util/SerializationHelper';
+import { Copy, ToScriptString, ToString } from './FieldSymbols';
+import { ObjectField } from './ObjectField';
 
 // Helps keep track of the current ink tool in use.
 export enum InkTool {
-    None = "none",
-    Pen = "pen",
-    Highlighter = "highlighter",
-    Eraser = "eraser",
-    Stamp = "stamp",
-    Write = "write",
-    PresentationPin = 'presentationpin'
+    None = 'none',
+    Pen = 'pen',
+    Highlighter = 'highlighter',
+    Eraser = 'eraser',
+    Stamp = 'stamp',
+    Write = 'write',
+    PresentationPin = 'presentationpin',
 }
 
-
 // Defines a point in an ink as a pair of x- and y-coordinates.
 export interface PointData {
     X: number;
@@ -54,15 +53,16 @@ export interface HandleLine {
 }
 
 const pointSchema = createSimpleSchema({
-    X: true, Y: true
+    X: true,
+    Y: true,
 });
 
 const strokeDataSchema = createSimpleSchema({
     pathData: list(object(pointSchema)),
-    "*": true
+    '*': true,
 });
 
-@Deserializable("ink")
+@Deserializable('ink')
 export class InkField extends ObjectField {
     @serializable(list(object(strokeDataSchema)))
     readonly inkData: InkData;
@@ -85,11 +85,11 @@ export class InkField extends ObjectField {
     }
 
     [ToScriptString]() {
-        return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}`) + "])";
+        return 'new InkField([' + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}}`) + '])';
     }
     [ToString]() {
-        return "InkField";
+        return 'InkField';
     }
 }
 
-ScriptingGlobals.add("InkField", InkField);
\ No newline at end of file
+ScriptingGlobals.add('InkField', InkField);
-- 
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/collections/TabDocView.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

